@arkade-os/sdk 0.4.7 → 0.4.9

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 (39) hide show
  1. package/dist/cjs/contracts/contractManager.js +59 -11
  2. package/dist/cjs/contracts/contractWatcher.js +21 -2
  3. package/dist/cjs/identity/seedIdentity.js +2 -2
  4. package/dist/cjs/index.js +9 -2
  5. package/dist/cjs/providers/expoIndexer.js +1 -0
  6. package/dist/cjs/providers/indexer.js +1 -0
  7. package/dist/cjs/utils/transactionHistory.js +2 -1
  8. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +249 -36
  9. package/dist/cjs/wallet/serviceWorker/wallet.js +286 -34
  10. package/dist/cjs/wallet/vtxo-manager.js +123 -86
  11. package/dist/cjs/wallet/wallet.js +140 -68
  12. package/dist/cjs/worker/errors.js +17 -0
  13. package/dist/cjs/worker/messageBus.js +14 -2
  14. package/dist/esm/contracts/contractManager.js +59 -11
  15. package/dist/esm/contracts/contractWatcher.js +21 -2
  16. package/dist/esm/identity/seedIdentity.js +2 -2
  17. package/dist/esm/index.js +3 -2
  18. package/dist/esm/providers/expoIndexer.js +1 -0
  19. package/dist/esm/providers/indexer.js +1 -0
  20. package/dist/esm/utils/transactionHistory.js +2 -1
  21. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +245 -35
  22. package/dist/esm/wallet/serviceWorker/wallet.js +286 -34
  23. package/dist/esm/wallet/vtxo-manager.js +123 -86
  24. package/dist/esm/wallet/wallet.js +140 -68
  25. package/dist/esm/worker/errors.js +12 -0
  26. package/dist/esm/worker/messageBus.js +14 -2
  27. package/dist/types/contracts/contractManager.d.ts +10 -0
  28. package/dist/types/identity/seedIdentity.d.ts +5 -2
  29. package/dist/types/index.d.ts +5 -4
  30. package/dist/types/repositories/serialization.d.ts +1 -0
  31. package/dist/types/utils/transactionHistory.d.ts +1 -1
  32. package/dist/types/wallet/index.d.ts +2 -0
  33. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +101 -7
  34. package/dist/types/wallet/serviceWorker/wallet.d.ts +16 -0
  35. package/dist/types/wallet/vtxo-manager.d.ts +29 -2
  36. package/dist/types/wallet/wallet.d.ts +10 -0
  37. package/dist/types/worker/errors.d.ts +6 -0
  38. package/dist/types/worker/messageBus.d.ts +6 -0
  39. package/package.json +1 -1
@@ -8,6 +8,7 @@ const delegator_1 = require("../providers/delegator");
8
8
  const identity_1 = require("../identity");
9
9
  const wallet_1 = require("../wallet/wallet");
10
10
  const base_1 = require("@scure/base");
11
+ const errors_1 = require("./errors");
11
12
  class MessageBus {
12
13
  constructor(walletRepository, contractRepository, { messageHandlers, tickIntervalMs = 10000, messageTimeoutMs = 30000, debug = false, buildServices, }) {
13
14
  this.walletRepository = walletRepository;
@@ -151,8 +152,12 @@ class MessageBus {
151
152
  identity,
152
153
  arkServerUrl: config.arkServer.url,
153
154
  arkServerPublicKey: config.arkServer.publicKey,
155
+ indexerUrl: config.indexerUrl,
156
+ esploraUrl: config.esploraUrl,
154
157
  storage,
155
158
  delegatorProvider,
159
+ settlementConfig: config.settlementConfig,
160
+ watcherConfig: config.watcherConfig,
156
161
  });
157
162
  return { wallet, arkProvider, readonlyWallet: wallet };
158
163
  }
@@ -162,8 +167,11 @@ class MessageBus {
162
167
  identity,
163
168
  arkServerUrl: config.arkServer.url,
164
169
  arkServerPublicKey: config.arkServer.publicKey,
170
+ indexerUrl: config.indexerUrl,
171
+ esploraUrl: config.esploraUrl,
165
172
  storage,
166
173
  delegatorProvider,
174
+ watcherConfig: config.watcherConfig,
167
175
  });
168
176
  return { readonlyWallet, arkProvider };
169
177
  }
@@ -183,6 +191,10 @@ class MessageBus {
183
191
  }
184
192
  async processMessage(event) {
185
193
  const { id, tag, broadcast } = event.data;
194
+ if (tag === "PING") {
195
+ event.source?.postMessage({ id, tag: "PONG" });
196
+ return;
197
+ }
186
198
  if (tag === "INITIALIZE_MESSAGE_BUS") {
187
199
  if (this.debug) {
188
200
  console.log("Init Command received");
@@ -207,7 +219,7 @@ class MessageBus {
207
219
  event.source?.postMessage({
208
220
  id,
209
221
  tag: tag ?? "unknown",
210
- error: new Error("MessageBus not initialized"),
222
+ error: new errors_1.MessageBusNotInitializedError(),
211
223
  });
212
224
  return;
213
225
  }
@@ -278,7 +290,7 @@ class MessageBus {
278
290
  return promise;
279
291
  return new Promise((resolve, reject) => {
280
292
  const timer = self.setTimeout(() => {
281
- reject(new Error(`Message handler timed out after ${this.messageTimeoutMs}ms (${label})`));
293
+ reject(new errors_1.ServiceWorkerTimeoutError(`Message handler timed out after ${this.messageTimeoutMs}ms (${label})`));
282
294
  }, this.messageTimeoutMs);
283
295
  promise.then((val) => {
284
296
  self.clearTimeout(timer);
@@ -71,9 +71,10 @@ export class ContractManager {
71
71
  }
72
72
  // Load persisted contracts
73
73
  const contracts = await this.config.contractRepository.getContracts();
74
- // fetch latest VTXOs for all contracts, ensure cache is up to date
74
+ // fetch all VTXOs (including spent/swept) for all contracts,
75
+ // so the repository has full history for transaction history and balance
75
76
  // TODO: what if the user has 1k contracts?
76
- await this.getVtxosForContracts(contracts);
77
+ await this.fetchContractVxosFromIndexer(contracts, true);
77
78
  // add all contracts to the watcher
78
79
  const now = Date.now();
79
80
  for (const contract of contracts) {
@@ -136,8 +137,8 @@ export class ContractManager {
136
137
  };
137
138
  // Persist
138
139
  await this.config.contractRepository.saveContract(contract);
139
- // ensure we have the latest VTXOs for this contract
140
- await this.getVtxosForContracts([contract]);
140
+ // fetch all VTXOs (including spent/swept) for this contract
141
+ await this.fetchContractVxosFromIndexer([contract], true);
141
142
  // Add to watcher
142
143
  await this.watcher.addContract(contract);
143
144
  return contract;
@@ -302,6 +303,14 @@ export class ContractManager {
302
303
  this.eventCallbacks.delete(callback);
303
304
  };
304
305
  }
306
+ /**
307
+ * Force a full VTXO refresh from the indexer for all contracts.
308
+ * Populates the wallet repository with complete VTXO history.
309
+ */
310
+ async refreshVtxos() {
311
+ const contracts = await this.config.contractRepository.getContracts();
312
+ await this.fetchContractVxosFromIndexer(contracts, true);
313
+ }
305
314
  /**
306
315
  * Check if currently watching.
307
316
  */
@@ -331,11 +340,13 @@ export class ContractManager {
331
340
  case "vtxo_spent":
332
341
  await this.fetchContractVxosFromIndexer([event.contract], true);
333
342
  break;
334
- case "connection_reset":
335
- // Refetch all VTXOs for all active contracts
343
+ case "connection_reset": {
344
+ // Refetch all VTXOs (including spent/swept) for all active
345
+ // contracts so the repo stays consistent with bootstrap state
336
346
  const activeWatchedContracts = this.watcher.getActiveContracts();
337
- await this.fetchContractVxosFromIndexer(activeWatchedContracts, false);
347
+ await this.fetchContractVxosFromIndexer(activeWatchedContracts, true);
338
348
  break;
349
+ }
339
350
  case "contract_expired":
340
351
  // just update DB
341
352
  await this.config.contractRepository.saveContract(event.contract);
@@ -362,11 +373,48 @@ export class ContractManager {
362
373
  return result;
363
374
  }
364
375
  async fetchContractVtxosBulk(contracts, includeSpent) {
365
- const result = new Map();
366
- await Promise.all(contracts.map(async (contract) => {
376
+ if (contracts.length === 0) {
377
+ return new Map();
378
+ }
379
+ // For a single contract, use the paginated path directly.
380
+ if (contracts.length === 1) {
381
+ const contract = contracts[0];
367
382
  const vtxos = await this.fetchContractVtxosPaginated(contract, includeSpent);
368
- result.set(contract.script, vtxos);
369
- }));
383
+ return new Map([[contract.script, vtxos]]);
384
+ }
385
+ // For multiple contracts, batch all scripts into a single indexer call
386
+ // per page to minimise round-trips. Results are keyed by script so we
387
+ // can distribute them back to the correct contract afterwards.
388
+ const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
389
+ const result = new Map(contracts.map((c) => [c.script, []]));
390
+ const scripts = contracts.map((c) => c.script);
391
+ const pageSize = 100;
392
+ const opts = includeSpent ? {} : { spendableOnly: true };
393
+ let pageIndex = 0;
394
+ let hasMore = true;
395
+ while (hasMore) {
396
+ const { vtxos, page } = await this.config.indexerProvider.getVtxos({
397
+ scripts,
398
+ ...opts,
399
+ pageIndex,
400
+ pageSize,
401
+ });
402
+ for (const vtxo of vtxos) {
403
+ // Match the VTXO back to its contract via the script field
404
+ // populated by the indexer.
405
+ if (!vtxo.script)
406
+ continue;
407
+ const contract = scriptToContract.get(vtxo.script);
408
+ if (!contract)
409
+ continue;
410
+ result.get(contract.script).push({
411
+ ...extendVtxoFromContract(vtxo, contract),
412
+ contractScript: contract.script,
413
+ });
414
+ }
415
+ hasMore = page ? vtxos.length === pageSize : false;
416
+ pageIndex++;
417
+ }
370
418
  return result;
371
419
  }
372
420
  async fetchContractVtxosPaginated(contract, includeSpent) {
@@ -410,8 +410,27 @@ export class ContractWatcher {
410
410
  }
411
411
  return;
412
412
  }
413
- this.subscriptionId =
414
- await this.config.indexerProvider.subscribeForScripts(scriptsToWatch, this.subscriptionId);
413
+ try {
414
+ this.subscriptionId =
415
+ await this.config.indexerProvider.subscribeForScripts(scriptsToWatch, this.subscriptionId);
416
+ }
417
+ catch (error) {
418
+ // If we sent a stale subscription ID that the server no longer
419
+ // recognises, clear it and retry to create a fresh subscription.
420
+ // The server currently returns HTTP 500 with a JSON body whose
421
+ // message field looks like "subscription <uuid> not found".
422
+ // All other errors (network failures, parse errors, etc.) are rethrown.
423
+ const isStale = error instanceof Error &&
424
+ /subscription\s+\S+\s+not\s+found/i.test(error.message);
425
+ if (this.subscriptionId && isStale) {
426
+ this.subscriptionId = undefined;
427
+ this.subscriptionId =
428
+ await this.config.indexerProvider.subscribeForScripts(scriptsToWatch);
429
+ }
430
+ else {
431
+ throw error;
432
+ }
433
+ }
415
434
  }
416
435
  /**
417
436
  * Main listening loop for subscription events.
@@ -102,7 +102,7 @@ export class SeedIdentity {
102
102
  static fromSeed(seed, opts) {
103
103
  const descriptor = hasDescriptor(opts)
104
104
  ? opts.descriptor
105
- : buildDescriptor(seed, opts.isMainnet);
105
+ : buildDescriptor(seed, opts.isMainnet ?? true);
106
106
  return new SeedIdentity(seed, descriptor);
107
107
  }
108
108
  async xOnlyPublicKey() {
@@ -190,7 +190,7 @@ export class MnemonicIdentity extends SeedIdentity {
190
190
  const seed = mnemonicToSeedSync(phrase, passphrase);
191
191
  const descriptor = hasDescriptor(opts)
192
192
  ? opts.descriptor
193
- : buildDescriptor(seed, opts.isMainnet);
193
+ : buildDescriptor(seed, opts.isMainnet ?? true);
194
194
  return new MnemonicIdentity(seed, descriptor);
195
195
  }
196
196
  }
package/dist/esm/index.js CHANGED
@@ -39,7 +39,8 @@ export * as asset from './extension/asset/index.js';
39
39
  // Contracts
40
40
  import { ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, } from './contracts/index.js';
41
41
  import { closeDatabase, openDatabase } from './repositories/indexedDB/manager.js';
42
- import { WalletMessageHandler } from './wallet/serviceWorker/wallet-message-handler.js';
42
+ import { WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError, } from './wallet/serviceWorker/wallet-message-handler.js';
43
+ import { MessageBusNotInitializedError, ServiceWorkerTimeoutError, } from './worker/errors.js';
43
44
  export {
44
45
  // Wallets
45
46
  Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, OnchainWallet, Ramps, VtxoManager, DelegatorManagerImpl, RestDelegatorProvider,
@@ -50,7 +51,7 @@ ArkAddress, DefaultVtxo, DelegateVtxo, VtxoScript, VHTLC,
50
51
  // Enums
51
52
  TxType, IndexerTxType, ChainTxType, SettlementEventType,
52
53
  // Service Worker
53
- setupServiceWorker, MessageBus, WalletMessageHandler, ServiceWorkerWallet, ServiceWorkerReadonlyWallet,
54
+ setupServiceWorker, MessageBus, WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError, MessageBusNotInitializedError, ServiceWorkerTimeoutError, ServiceWorkerWallet, ServiceWorkerReadonlyWallet,
54
55
  // Tapscript
55
56
  decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder,
56
57
  // Ark PSBT fields
@@ -28,6 +28,7 @@ function convertVtxo(vtxo) {
28
28
  createdAt: new Date(Number(vtxo.createdAt) * 1000),
29
29
  isUnrolled: vtxo.isUnrolled,
30
30
  isSpent: vtxo.isSpent,
31
+ script: vtxo.script,
31
32
  assets: vtxo.assets?.map((a) => ({
32
33
  assetId: a.assetId,
33
34
  amount: Number(a.amount),
@@ -402,6 +402,7 @@ function convertVtxo(vtxo) {
402
402
  createdAt: new Date(Number(vtxo.createdAt) * 1000),
403
403
  isUnrolled: vtxo.isUnrolled,
404
404
  isSpent: vtxo.isSpent,
405
+ script: vtxo.script,
405
406
  assets: vtxo.assets?.map((a) => ({
406
407
  assetId: a.assetId,
407
408
  amount: Number(a.amount),
@@ -115,7 +115,8 @@ export async function buildTransactionHistory(vtxos, allBoardingTxs, commitments
115
115
  txAmount = spentAmount;
116
116
  // TODO: fetch the vtxo with /v1/indexer/vtxos?outpoints=<vtxo.arkTxid:0> to know when the tx was made
117
117
  txTime = getTxCreatedAt
118
- ? await getTxCreatedAt(vtxo.arkTxId)
118
+ ? ((await getTxCreatedAt(vtxo.arkTxId)) ??
119
+ vtxo.createdAt.getTime() + 1)
119
120
  : vtxo.createdAt.getTime() + 1;
120
121
  }
121
122
  const assets = subtractAssets(allSpent, changes);