@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.
- package/dist/cjs/contracts/contractManager.js +59 -11
- package/dist/cjs/contracts/contractWatcher.js +21 -2
- package/dist/cjs/identity/seedIdentity.js +2 -2
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/providers/expoIndexer.js +1 -0
- package/dist/cjs/providers/indexer.js +1 -0
- package/dist/cjs/utils/transactionHistory.js +2 -1
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +249 -36
- package/dist/cjs/wallet/serviceWorker/wallet.js +286 -34
- package/dist/cjs/wallet/vtxo-manager.js +123 -86
- package/dist/cjs/wallet/wallet.js +140 -68
- package/dist/cjs/worker/errors.js +17 -0
- package/dist/cjs/worker/messageBus.js +14 -2
- package/dist/esm/contracts/contractManager.js +59 -11
- package/dist/esm/contracts/contractWatcher.js +21 -2
- package/dist/esm/identity/seedIdentity.js +2 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/providers/expoIndexer.js +1 -0
- package/dist/esm/providers/indexer.js +1 -0
- package/dist/esm/utils/transactionHistory.js +2 -1
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +245 -35
- package/dist/esm/wallet/serviceWorker/wallet.js +286 -34
- package/dist/esm/wallet/vtxo-manager.js +123 -86
- package/dist/esm/wallet/wallet.js +140 -68
- package/dist/esm/worker/errors.js +12 -0
- package/dist/esm/worker/messageBus.js +14 -2
- package/dist/types/contracts/contractManager.d.ts +10 -0
- package/dist/types/identity/seedIdentity.d.ts +5 -2
- package/dist/types/index.d.ts +5 -4
- package/dist/types/repositories/serialization.d.ts +1 -0
- package/dist/types/utils/transactionHistory.d.ts +1 -1
- package/dist/types/wallet/index.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +101 -7
- package/dist/types/wallet/serviceWorker/wallet.d.ts +16 -0
- package/dist/types/wallet/vtxo-manager.d.ts +29 -2
- package/dist/types/wallet/wallet.d.ts +10 -0
- package/dist/types/worker/errors.d.ts +6 -0
- package/dist/types/worker/messageBus.d.ts +6 -0
- 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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
//
|
|
140
|
-
await this.
|
|
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
|
|
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,
|
|
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
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
414
|
-
|
|
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
|
|
@@ -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);
|