@arkade-os/sdk 0.3.13 → 0.4.0-next.1
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/README.md +586 -54
- package/dist/cjs/asset/assetGroup.js +141 -0
- package/dist/cjs/asset/assetId.js +88 -0
- package/dist/cjs/asset/assetInput.js +204 -0
- package/dist/cjs/asset/assetOutput.js +159 -0
- package/dist/cjs/asset/assetRef.js +82 -0
- package/dist/cjs/asset/index.js +24 -0
- package/dist/cjs/asset/metadata.js +172 -0
- package/dist/cjs/asset/packet.js +164 -0
- package/dist/cjs/asset/types.js +25 -0
- package/dist/cjs/asset/utils.js +105 -0
- package/dist/cjs/bip322/index.js +270 -0
- package/dist/cjs/contracts/arkcontract.js +148 -0
- package/dist/cjs/contracts/contractManager.js +436 -0
- package/dist/cjs/contracts/contractWatcher.js +567 -0
- package/dist/cjs/contracts/handlers/default.js +85 -0
- package/dist/cjs/contracts/handlers/delegate.js +89 -0
- package/dist/cjs/contracts/handlers/helpers.js +105 -0
- package/dist/cjs/contracts/handlers/index.js +19 -0
- package/dist/cjs/contracts/handlers/registry.js +89 -0
- package/dist/cjs/contracts/handlers/vhtlc.js +193 -0
- package/dist/cjs/contracts/index.js +41 -0
- package/dist/cjs/contracts/types.js +2 -0
- package/dist/cjs/forfeit.js +12 -8
- package/dist/cjs/identity/index.js +1 -0
- package/dist/cjs/identity/seedIdentity.js +255 -0
- package/dist/cjs/index.js +72 -14
- package/dist/cjs/intent/index.js +47 -11
- package/dist/cjs/providers/ark.js +7 -0
- package/dist/cjs/providers/delegator.js +66 -0
- package/dist/cjs/providers/expoIndexer.js +5 -0
- package/dist/cjs/providers/indexer.js +68 -1
- package/dist/cjs/providers/utils.js +1 -0
- package/dist/cjs/repositories/contractRepository.js +0 -103
- package/dist/cjs/repositories/inMemory/contractRepository.js +55 -0
- package/dist/cjs/repositories/inMemory/walletRepository.js +80 -0
- package/dist/cjs/repositories/index.js +16 -0
- package/dist/cjs/repositories/indexedDB/contractRepository.js +187 -0
- package/dist/cjs/repositories/indexedDB/db.js +19 -0
- package/dist/cjs/repositories/indexedDB/manager.js +97 -0
- package/dist/cjs/repositories/indexedDB/schema.js +159 -0
- package/dist/cjs/repositories/indexedDB/walletRepository.js +338 -0
- package/dist/cjs/repositories/indexedDB/websqlAdapter.js +144 -0
- package/dist/cjs/repositories/migrations/contractRepositoryImpl.js +127 -0
- package/dist/cjs/repositories/migrations/fromStorageAdapter.js +66 -0
- package/dist/cjs/repositories/migrations/walletRepositoryImpl.js +180 -0
- package/dist/cjs/repositories/realm/contractRepository.js +120 -0
- package/dist/cjs/repositories/realm/index.js +9 -0
- package/dist/cjs/repositories/realm/schemas.js +108 -0
- package/dist/cjs/repositories/realm/types.js +7 -0
- package/dist/cjs/repositories/realm/walletRepository.js +273 -0
- package/dist/cjs/repositories/serialization.js +49 -0
- package/dist/cjs/repositories/sqlite/contractRepository.js +139 -0
- package/dist/cjs/repositories/sqlite/index.js +7 -0
- package/dist/cjs/repositories/sqlite/types.js +2 -0
- package/dist/cjs/repositories/sqlite/walletRepository.js +328 -0
- package/dist/cjs/repositories/walletRepository.js +0 -169
- package/dist/cjs/script/base.js +54 -0
- package/dist/cjs/script/delegate.js +49 -0
- package/dist/cjs/storage/asyncStorage.js +4 -1
- package/dist/cjs/storage/fileSystem.js +3 -0
- package/dist/cjs/storage/inMemory.js +3 -0
- package/dist/cjs/storage/indexedDB.js +5 -1
- package/dist/cjs/storage/localStorage.js +3 -0
- package/dist/cjs/utils/arkTransaction.js +16 -0
- package/dist/cjs/utils/transactionHistory.js +50 -0
- package/dist/cjs/wallet/asset-manager.js +338 -0
- package/dist/cjs/wallet/asset.js +117 -0
- package/dist/cjs/wallet/batch.js +1 -1
- package/dist/cjs/wallet/delegator.js +235 -0
- package/dist/cjs/wallet/expo/background.js +133 -0
- package/dist/cjs/wallet/expo/index.js +9 -0
- package/dist/cjs/wallet/expo/wallet.js +231 -0
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +568 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +383 -102
- package/dist/cjs/wallet/utils.js +58 -0
- package/dist/cjs/wallet/validation.js +151 -0
- package/dist/cjs/wallet/vtxo-manager.js +8 -1
- package/dist/cjs/wallet/wallet.js +702 -260
- package/dist/cjs/worker/browser/service-worker-manager.js +82 -0
- package/dist/cjs/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
- package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +78 -0
- package/dist/cjs/worker/expo/index.js +12 -0
- package/dist/cjs/worker/expo/processors/contractPollProcessor.js +61 -0
- package/dist/cjs/worker/expo/processors/index.js +6 -0
- package/dist/cjs/worker/expo/taskQueue.js +41 -0
- package/dist/cjs/worker/expo/taskRunner.js +57 -0
- package/dist/cjs/worker/messageBus.js +252 -0
- package/dist/esm/asset/assetGroup.js +137 -0
- package/dist/esm/asset/assetId.js +84 -0
- package/dist/esm/asset/assetInput.js +199 -0
- package/dist/esm/asset/assetOutput.js +154 -0
- package/dist/esm/asset/assetRef.js +78 -0
- package/dist/esm/asset/index.js +8 -0
- package/dist/esm/asset/metadata.js +167 -0
- package/dist/esm/asset/packet.js +159 -0
- package/dist/esm/asset/types.js +22 -0
- package/dist/esm/asset/utils.js +99 -0
- package/dist/esm/bip322/index.js +267 -0
- package/dist/esm/contracts/arkcontract.js +141 -0
- package/dist/esm/contracts/contractManager.js +432 -0
- package/dist/esm/contracts/contractWatcher.js +563 -0
- package/dist/esm/contracts/handlers/default.js +82 -0
- package/dist/esm/contracts/handlers/delegate.js +86 -0
- package/dist/esm/contracts/handlers/helpers.js +66 -0
- package/dist/esm/contracts/handlers/index.js +12 -0
- package/dist/esm/contracts/handlers/registry.js +86 -0
- package/dist/esm/contracts/handlers/vhtlc.js +190 -0
- package/dist/esm/contracts/index.js +13 -0
- package/dist/esm/contracts/types.js +1 -0
- package/dist/esm/forfeit.js +11 -8
- package/dist/esm/identity/index.js +1 -0
- package/dist/esm/identity/seedIdentity.js +249 -0
- package/dist/esm/index.js +28 -15
- package/dist/esm/intent/index.js +44 -9
- package/dist/esm/providers/ark.js +7 -0
- package/dist/esm/providers/delegator.js +62 -0
- package/dist/esm/providers/expoIndexer.js +5 -0
- package/dist/esm/providers/indexer.js +68 -1
- package/dist/esm/providers/utils.js +1 -0
- package/dist/esm/repositories/contractRepository.js +1 -101
- package/dist/esm/repositories/inMemory/contractRepository.js +51 -0
- package/dist/esm/repositories/inMemory/walletRepository.js +76 -0
- package/dist/esm/repositories/index.js +8 -0
- package/dist/esm/repositories/indexedDB/contractRepository.js +183 -0
- package/dist/esm/repositories/indexedDB/db.js +4 -0
- package/dist/esm/repositories/indexedDB/manager.js +92 -0
- package/dist/esm/repositories/indexedDB/schema.js +155 -0
- package/dist/esm/repositories/indexedDB/walletRepository.js +334 -0
- package/dist/esm/repositories/indexedDB/websqlAdapter.js +138 -0
- package/dist/esm/repositories/migrations/contractRepositoryImpl.js +121 -0
- package/dist/esm/repositories/migrations/fromStorageAdapter.js +58 -0
- package/dist/esm/repositories/migrations/walletRepositoryImpl.js +176 -0
- package/dist/esm/repositories/realm/contractRepository.js +116 -0
- package/dist/esm/repositories/realm/index.js +3 -0
- package/dist/esm/repositories/realm/schemas.js +105 -0
- package/dist/esm/repositories/realm/types.js +6 -0
- package/dist/esm/repositories/realm/walletRepository.js +269 -0
- package/dist/esm/repositories/serialization.js +40 -0
- package/dist/esm/repositories/sqlite/contractRepository.js +135 -0
- package/dist/esm/repositories/sqlite/index.js +2 -0
- package/dist/esm/repositories/sqlite/types.js +1 -0
- package/dist/esm/repositories/sqlite/walletRepository.js +324 -0
- package/dist/esm/repositories/walletRepository.js +1 -167
- package/dist/esm/script/base.js +21 -1
- package/dist/esm/script/delegate.js +46 -0
- package/dist/esm/storage/asyncStorage.js +4 -1
- package/dist/esm/storage/fileSystem.js +3 -0
- package/dist/esm/storage/inMemory.js +3 -0
- package/dist/esm/storage/indexedDB.js +5 -1
- package/dist/esm/storage/localStorage.js +3 -0
- package/dist/esm/utils/arkTransaction.js +15 -0
- package/dist/esm/utils/transactionHistory.js +50 -0
- package/dist/esm/wallet/asset-manager.js +333 -0
- package/dist/esm/wallet/asset.js +111 -0
- package/dist/esm/wallet/batch.js +1 -1
- package/dist/esm/wallet/delegator.js +231 -0
- package/dist/esm/wallet/expo/background.js +128 -0
- package/dist/esm/wallet/expo/index.js +2 -0
- package/dist/esm/wallet/expo/wallet.js +194 -0
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +564 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +382 -101
- package/dist/esm/wallet/utils.js +54 -0
- package/dist/esm/wallet/validation.js +139 -0
- package/dist/esm/wallet/vtxo-manager.js +8 -1
- package/dist/esm/wallet/wallet.js +704 -229
- package/dist/esm/worker/browser/service-worker-manager.js +76 -0
- package/dist/esm/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
- package/dist/esm/worker/expo/asyncStorageTaskQueue.js +74 -0
- package/dist/esm/worker/expo/index.js +4 -0
- package/dist/esm/worker/expo/processors/contractPollProcessor.js +58 -0
- package/dist/esm/worker/expo/processors/index.js +1 -0
- package/dist/esm/worker/expo/taskQueue.js +37 -0
- package/dist/esm/worker/expo/taskRunner.js +54 -0
- package/dist/esm/worker/messageBus.js +248 -0
- package/dist/types/asset/assetGroup.d.ts +28 -0
- package/dist/types/asset/assetId.d.ts +19 -0
- package/dist/types/asset/assetInput.d.ts +46 -0
- package/dist/types/asset/assetOutput.d.ts +39 -0
- package/dist/types/asset/assetRef.d.ts +25 -0
- package/dist/types/asset/index.d.ts +8 -0
- package/dist/types/asset/metadata.d.ts +37 -0
- package/dist/types/asset/packet.d.ts +27 -0
- package/dist/types/asset/types.d.ts +18 -0
- package/dist/types/asset/utils.d.ts +21 -0
- package/dist/types/bip322/index.d.ts +55 -0
- package/dist/types/contracts/arkcontract.d.ts +101 -0
- package/dist/types/contracts/contractManager.d.ts +331 -0
- package/dist/types/contracts/contractWatcher.d.ts +192 -0
- package/dist/types/contracts/handlers/default.d.ts +19 -0
- package/dist/types/contracts/handlers/delegate.d.ts +21 -0
- package/dist/types/contracts/handlers/helpers.d.ts +18 -0
- package/dist/types/contracts/handlers/index.d.ts +7 -0
- package/dist/types/contracts/handlers/registry.d.ts +65 -0
- package/dist/types/contracts/handlers/vhtlc.d.ts +32 -0
- package/dist/types/contracts/index.d.ts +14 -0
- package/dist/types/contracts/types.d.ts +222 -0
- package/dist/types/forfeit.d.ts +2 -1
- package/dist/types/identity/index.d.ts +1 -0
- package/dist/types/identity/seedIdentity.d.ts +128 -0
- package/dist/types/index.d.ts +22 -12
- package/dist/types/intent/index.d.ts +15 -1
- package/dist/types/providers/ark.d.ts +11 -2
- package/dist/types/providers/delegator.d.ts +29 -0
- package/dist/types/providers/indexer.d.ts +11 -1
- package/dist/types/repositories/contractRepository.d.ts +30 -19
- package/dist/types/repositories/inMemory/contractRepository.d.ts +17 -0
- package/dist/types/repositories/inMemory/walletRepository.d.ts +26 -0
- package/dist/types/repositories/index.d.ts +7 -0
- package/dist/types/repositories/indexedDB/contractRepository.d.ts +21 -0
- package/dist/types/repositories/indexedDB/db.d.ts +4 -0
- package/dist/types/repositories/indexedDB/manager.d.ts +22 -0
- package/dist/types/repositories/indexedDB/schema.d.ts +8 -0
- package/dist/types/repositories/indexedDB/walletRepository.d.ts +25 -0
- package/dist/types/repositories/indexedDB/websqlAdapter.d.ts +49 -0
- package/dist/types/repositories/migrations/contractRepositoryImpl.d.ts +24 -0
- package/dist/types/repositories/migrations/fromStorageAdapter.d.ts +19 -0
- package/dist/types/repositories/migrations/walletRepositoryImpl.d.ts +27 -0
- package/dist/types/repositories/realm/contractRepository.d.ts +24 -0
- package/dist/types/repositories/realm/index.d.ts +4 -0
- package/dist/types/repositories/realm/schemas.d.ts +208 -0
- package/dist/types/repositories/realm/types.d.ts +16 -0
- package/dist/types/repositories/realm/walletRepository.d.ts +31 -0
- package/dist/types/repositories/serialization.d.ts +40 -0
- package/dist/types/repositories/sqlite/contractRepository.d.ts +33 -0
- package/dist/types/repositories/sqlite/index.d.ts +3 -0
- package/dist/types/repositories/sqlite/types.d.ts +18 -0
- package/dist/types/repositories/sqlite/walletRepository.d.ts +40 -0
- package/dist/types/repositories/walletRepository.d.ts +13 -24
- package/dist/types/script/base.d.ts +1 -0
- package/dist/types/script/delegate.d.ts +36 -0
- package/dist/types/storage/asyncStorage.d.ts +4 -0
- package/dist/types/storage/fileSystem.d.ts +3 -0
- package/dist/types/storage/inMemory.d.ts +3 -0
- package/dist/types/storage/index.d.ts +3 -0
- package/dist/types/storage/indexedDB.d.ts +3 -0
- package/dist/types/storage/localStorage.d.ts +3 -0
- package/dist/types/utils/arkTransaction.d.ts +6 -0
- package/dist/types/wallet/asset-manager.d.ts +78 -0
- package/dist/types/wallet/asset.d.ts +21 -0
- package/dist/types/wallet/batch.d.ts +1 -1
- package/dist/types/wallet/delegator.d.ts +24 -0
- package/dist/types/wallet/expo/background.d.ts +66 -0
- package/dist/types/wallet/expo/index.d.ts +4 -0
- package/dist/types/wallet/expo/wallet.d.ts +97 -0
- package/dist/types/wallet/index.d.ts +75 -2
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +366 -0
- package/dist/types/wallet/serviceWorker/wallet.d.ts +20 -11
- package/dist/types/wallet/utils.d.ts +12 -1
- package/dist/types/wallet/validation.d.ts +24 -0
- package/dist/types/wallet/wallet.d.ts +111 -17
- package/dist/types/worker/browser/service-worker-manager.d.ts +21 -0
- package/dist/types/{wallet/serviceWorker → worker/browser}/utils.d.ts +2 -1
- package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +46 -0
- package/dist/types/worker/expo/index.d.ts +7 -0
- package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +14 -0
- package/dist/types/worker/expo/processors/index.d.ts +1 -0
- package/dist/types/worker/expo/taskQueue.d.ts +50 -0
- package/dist/types/worker/expo/taskRunner.d.ts +42 -0
- package/dist/types/worker/messageBus.d.ts +109 -0
- package/package.json +69 -11
- package/dist/cjs/wallet/serviceWorker/request.js +0 -78
- package/dist/cjs/wallet/serviceWorker/response.js +0 -222
- package/dist/cjs/wallet/serviceWorker/worker.js +0 -655
- package/dist/esm/wallet/serviceWorker/request.js +0 -75
- package/dist/esm/wallet/serviceWorker/response.js +0 -219
- package/dist/esm/wallet/serviceWorker/worker.js +0 -651
- package/dist/types/wallet/serviceWorker/request.d.ts +0 -74
- package/dist/types/wallet/serviceWorker/response.d.ts +0 -123
- package/dist/types/wallet/serviceWorker/worker.d.ts +0 -53
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContractWatcher = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Watches multiple contracts for VTXO changes with resilient connection handling.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Automatic reconnection with exponential backoff
|
|
9
|
+
* - Failsafe polling to catch missed events
|
|
10
|
+
* - Polls immediately after (re)connection to sync state
|
|
11
|
+
* - Graceful handling of subscription failures
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const watcher = new ContractWatcher({
|
|
16
|
+
* indexerProvider: wallet.indexerProvider,
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Add the wallet's default contract
|
|
20
|
+
* await watcher.addContract(defaultContract);
|
|
21
|
+
*
|
|
22
|
+
* // Add additional contracts (swaps, etc.)
|
|
23
|
+
* await watcher.addContract(swapContract);
|
|
24
|
+
*
|
|
25
|
+
* // Start watching for events
|
|
26
|
+
* const stop = await watcher.startWatching((event) => {
|
|
27
|
+
* console.log(`${event.type} on contract ${event.contractScript}`);
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Later: stop watching
|
|
31
|
+
* stop();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
class ContractWatcher {
|
|
35
|
+
constructor(config) {
|
|
36
|
+
this.contracts = new Map();
|
|
37
|
+
this.isWatching = false;
|
|
38
|
+
this.connectionState = "disconnected";
|
|
39
|
+
this.reconnectAttempts = 0;
|
|
40
|
+
this.config = {
|
|
41
|
+
failsafePollIntervalMs: 60000, // 1 minute
|
|
42
|
+
reconnectDelayMs: 1000, // 1 second
|
|
43
|
+
maxReconnectDelayMs: 30000, // 30 seconds
|
|
44
|
+
maxReconnectAttempts: 0, // unlimited
|
|
45
|
+
...config,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Add a contract to be watched.
|
|
50
|
+
*
|
|
51
|
+
* Active contracts are immediately subscribed. All contracts are polled
|
|
52
|
+
* to discover any existing VTXOs (which may cause them to be watched
|
|
53
|
+
* even if inactive).
|
|
54
|
+
*/
|
|
55
|
+
async addContract(contract) {
|
|
56
|
+
const state = {
|
|
57
|
+
contract,
|
|
58
|
+
lastKnownVtxos: new Map(),
|
|
59
|
+
};
|
|
60
|
+
this.contracts.set(contract.script, state);
|
|
61
|
+
// If we're already watching, poll to discover VTXOs and update subscription
|
|
62
|
+
if (this.isWatching) {
|
|
63
|
+
// Poll first to discover VTXOs (may affect whether we watch this contract)
|
|
64
|
+
await this.pollContracts([contract.script]);
|
|
65
|
+
// Update subscription based on active state and VTXOs
|
|
66
|
+
await this.tryUpdateSubscription();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Update an existing contract.
|
|
71
|
+
*/
|
|
72
|
+
async updateContract(contract) {
|
|
73
|
+
const existing = this.contracts.get(contract.script);
|
|
74
|
+
if (!existing) {
|
|
75
|
+
throw new Error(`Contract ${contract.script} not found`);
|
|
76
|
+
}
|
|
77
|
+
existing.contract = contract;
|
|
78
|
+
if (this.isWatching) {
|
|
79
|
+
await this.tryUpdateSubscription();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Remove a contract from watching.
|
|
84
|
+
*/
|
|
85
|
+
async removeContract(contractScript) {
|
|
86
|
+
const state = this.contracts.get(contractScript);
|
|
87
|
+
if (state) {
|
|
88
|
+
this.contracts.delete(contractScript);
|
|
89
|
+
if (this.isWatching) {
|
|
90
|
+
await this.tryUpdateSubscription();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get all in-memory contracts.
|
|
96
|
+
*/
|
|
97
|
+
getAllContracts() {
|
|
98
|
+
return Array.from(this.contracts.values()).map((s) => s.contract);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all active in-memory contracts.
|
|
102
|
+
*/
|
|
103
|
+
getActiveContracts() {
|
|
104
|
+
return this.getAllContracts().filter((c) => c.state === "active");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get scripts that should be watched.
|
|
108
|
+
*
|
|
109
|
+
* Returns scripts for:
|
|
110
|
+
* - All active contracts
|
|
111
|
+
* - All contracts with known VTXOs (regardless of state)
|
|
112
|
+
*
|
|
113
|
+
* This ensures we continue monitoring contracts even after they're
|
|
114
|
+
* deactivated, as long as they have unspent VTXOs.
|
|
115
|
+
*/
|
|
116
|
+
getScriptsToWatch() {
|
|
117
|
+
const scripts = new Set();
|
|
118
|
+
for (const [, state] of this.contracts) {
|
|
119
|
+
// Always watch active contracts
|
|
120
|
+
if (state.contract.state === "active") {
|
|
121
|
+
scripts.add(state.contract.script);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Also watch inactive/expired contracts that have VTXOs
|
|
125
|
+
if (state.lastKnownVtxos.size > 0) {
|
|
126
|
+
scripts.add(state.contract.script);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return Array.from(scripts);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get VTXOs for contracts, grouped by contract script.
|
|
133
|
+
* Uses Repository.
|
|
134
|
+
*/
|
|
135
|
+
async getContractVtxos(options) {
|
|
136
|
+
const { contractScripts, includeSpent } = options;
|
|
137
|
+
const repo = this.config.walletRepository;
|
|
138
|
+
const contractsToQuery = Array.from(this.contracts.values());
|
|
139
|
+
const asyncResults = contractsToQuery
|
|
140
|
+
.filter((_) => {
|
|
141
|
+
if (contractScripts &&
|
|
142
|
+
!contractScripts.includes(_.contract.script))
|
|
143
|
+
return false;
|
|
144
|
+
return true;
|
|
145
|
+
})
|
|
146
|
+
.map(async (state) => {
|
|
147
|
+
// Use contract address as cache key
|
|
148
|
+
const cached = await repo.getVtxos(state.contract.address);
|
|
149
|
+
if (cached.length > 0) {
|
|
150
|
+
// Convert to ContractVtxo with contractScript
|
|
151
|
+
const contractVtxos = cached.map((v) => ({
|
|
152
|
+
...v,
|
|
153
|
+
contractScript: state.contract.script,
|
|
154
|
+
}));
|
|
155
|
+
const filtered = includeSpent
|
|
156
|
+
? contractVtxos
|
|
157
|
+
: contractVtxos.filter((v) => !v.isSpent);
|
|
158
|
+
return [[state.contract.script, filtered]];
|
|
159
|
+
}
|
|
160
|
+
return [];
|
|
161
|
+
});
|
|
162
|
+
const results = await Promise.all(asyncResults);
|
|
163
|
+
return new Map(results.flat(1));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Start watching for VTXO events across all active contracts.
|
|
167
|
+
*/
|
|
168
|
+
async startWatching(callback) {
|
|
169
|
+
if (this.isWatching) {
|
|
170
|
+
throw new Error("Already watching");
|
|
171
|
+
}
|
|
172
|
+
this.eventCallback = callback;
|
|
173
|
+
this.isWatching = true;
|
|
174
|
+
this.abortController = new AbortController();
|
|
175
|
+
this.reconnectAttempts = 0;
|
|
176
|
+
// Start connection
|
|
177
|
+
await this.connect();
|
|
178
|
+
// Start failsafe polling
|
|
179
|
+
this.startFailsafePolling();
|
|
180
|
+
return () => this.stopWatching();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Stop watching for events.
|
|
184
|
+
*/
|
|
185
|
+
async stopWatching() {
|
|
186
|
+
this.isWatching = false;
|
|
187
|
+
this.connectionState = "disconnected";
|
|
188
|
+
this.abortController?.abort();
|
|
189
|
+
// Clear timers
|
|
190
|
+
if (this.reconnectTimeoutId) {
|
|
191
|
+
clearTimeout(this.reconnectTimeoutId);
|
|
192
|
+
this.reconnectTimeoutId = undefined;
|
|
193
|
+
}
|
|
194
|
+
if (this.failsafePollIntervalId) {
|
|
195
|
+
clearInterval(this.failsafePollIntervalId);
|
|
196
|
+
this.failsafePollIntervalId = undefined;
|
|
197
|
+
}
|
|
198
|
+
// Unsubscribe
|
|
199
|
+
if (this.subscriptionId) {
|
|
200
|
+
try {
|
|
201
|
+
await this.config.indexerProvider.unsubscribeForScripts(this.subscriptionId);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Ignore unsubscribe errors
|
|
205
|
+
}
|
|
206
|
+
this.subscriptionId = undefined;
|
|
207
|
+
}
|
|
208
|
+
this.eventCallback = undefined;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if currently watching.
|
|
212
|
+
*/
|
|
213
|
+
isCurrentlyWatching() {
|
|
214
|
+
return this.isWatching;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get current connection state.
|
|
218
|
+
*/
|
|
219
|
+
getConnectionState() {
|
|
220
|
+
return this.connectionState;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Force a poll of all active contracts.
|
|
224
|
+
* Useful for manual refresh or after app resume.
|
|
225
|
+
*/
|
|
226
|
+
async forcePoll() {
|
|
227
|
+
if (!this.isWatching)
|
|
228
|
+
return;
|
|
229
|
+
await this.pollAllContracts();
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Check for expired contracts, update their state, and emit events.
|
|
233
|
+
*/
|
|
234
|
+
checkExpiredContracts() {
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
const expired = [];
|
|
237
|
+
for (const state of this.contracts.values()) {
|
|
238
|
+
const contract = state.contract;
|
|
239
|
+
if (contract.state === "active" &&
|
|
240
|
+
contract.expiresAt &&
|
|
241
|
+
contract.expiresAt <= now) {
|
|
242
|
+
contract.state = "inactive";
|
|
243
|
+
expired.push(contract);
|
|
244
|
+
this.eventCallback?.({
|
|
245
|
+
type: "contract_expired",
|
|
246
|
+
contractScript: contract.script,
|
|
247
|
+
contract,
|
|
248
|
+
timestamp: now,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Connect to the subscription.
|
|
255
|
+
*/
|
|
256
|
+
async connect() {
|
|
257
|
+
if (!this.isWatching)
|
|
258
|
+
return;
|
|
259
|
+
this.connectionState = "connecting";
|
|
260
|
+
try {
|
|
261
|
+
await this.updateSubscription();
|
|
262
|
+
// Poll immediately after connection to sync state
|
|
263
|
+
await this.pollAllContracts();
|
|
264
|
+
this.connectionState = "connected";
|
|
265
|
+
this.reconnectAttempts = 0;
|
|
266
|
+
// Start listening
|
|
267
|
+
this.listenLoop().catch((e) => {
|
|
268
|
+
// This is handled asynchronously otherwise `connect()` would hang
|
|
269
|
+
// indefinitely and block the caller.
|
|
270
|
+
// Error management must be implemented to ensure the connection
|
|
271
|
+
// is restored and events are fired.
|
|
272
|
+
console.error(e);
|
|
273
|
+
this.connectionState = "disconnected";
|
|
274
|
+
this.eventCallback?.({
|
|
275
|
+
type: "connection_reset",
|
|
276
|
+
timestamp: Date.now(),
|
|
277
|
+
});
|
|
278
|
+
this.scheduleReconnect();
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
console.error("ContractWatcher connection failed:", error);
|
|
283
|
+
this.connectionState = "disconnected";
|
|
284
|
+
this.eventCallback?.({
|
|
285
|
+
type: "connection_reset",
|
|
286
|
+
timestamp: Date.now(),
|
|
287
|
+
});
|
|
288
|
+
this.scheduleReconnect();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Schedule a reconnection attempt.
|
|
293
|
+
*/
|
|
294
|
+
scheduleReconnect() {
|
|
295
|
+
if (!this.isWatching)
|
|
296
|
+
return;
|
|
297
|
+
// Check max attempts
|
|
298
|
+
if (this.config.maxReconnectAttempts > 0 &&
|
|
299
|
+
this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
300
|
+
console.error(`ContractWatcher: Max reconnection attempts (${this.config.maxReconnectAttempts}) reached`);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
this.connectionState = "reconnecting";
|
|
304
|
+
this.reconnectAttempts++;
|
|
305
|
+
// Calculate delay with exponential backoff
|
|
306
|
+
const delay = Math.min(this.config.reconnectDelayMs *
|
|
307
|
+
Math.pow(2, this.reconnectAttempts - 1), this.config.maxReconnectDelayMs);
|
|
308
|
+
this.reconnectTimeoutId = setTimeout(() => {
|
|
309
|
+
this.reconnectTimeoutId = undefined;
|
|
310
|
+
this.connect();
|
|
311
|
+
}, delay);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Start the failsafe polling interval.
|
|
315
|
+
*/
|
|
316
|
+
startFailsafePolling() {
|
|
317
|
+
if (this.failsafePollIntervalId) {
|
|
318
|
+
clearInterval(this.failsafePollIntervalId);
|
|
319
|
+
}
|
|
320
|
+
this.failsafePollIntervalId = setInterval(() => {
|
|
321
|
+
if (this.isWatching) {
|
|
322
|
+
this.pollAllContracts().catch((error) => {
|
|
323
|
+
console.error("ContractWatcher failsafe poll failed:", error);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}, this.config.failsafePollIntervalMs);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Poll all active contracts for current state.
|
|
330
|
+
*/
|
|
331
|
+
async pollAllContracts() {
|
|
332
|
+
const activeScripts = this.getActiveContracts().map((c) => c.script);
|
|
333
|
+
if (activeScripts.length === 0)
|
|
334
|
+
return;
|
|
335
|
+
await this.pollContracts(activeScripts);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Poll specific contracts and emit events for changes.
|
|
339
|
+
*/
|
|
340
|
+
async pollContracts(contractScripts) {
|
|
341
|
+
if (!this.eventCallback)
|
|
342
|
+
return;
|
|
343
|
+
const now = Date.now();
|
|
344
|
+
try {
|
|
345
|
+
// Load all the VTXOs for these contracts, from DB
|
|
346
|
+
const vtxosMap = await this.getContractVtxos({
|
|
347
|
+
contractScripts,
|
|
348
|
+
includeSpent: false, // only spendable ones!
|
|
349
|
+
});
|
|
350
|
+
for (const contractScript of contractScripts) {
|
|
351
|
+
const state = this.contracts.get(contractScript);
|
|
352
|
+
if (!state)
|
|
353
|
+
continue;
|
|
354
|
+
const currentVtxos = vtxosMap.get(contractScript) || [];
|
|
355
|
+
const currentKeys = new Set(currentVtxos.map((v) => `${v.txid}:${v.vout}`));
|
|
356
|
+
// Find new VTXOs and add them to the contract's state
|
|
357
|
+
const newVtxos = [];
|
|
358
|
+
for (const vtxo of currentVtxos) {
|
|
359
|
+
const key = `${vtxo.txid}:${vtxo.vout}`;
|
|
360
|
+
if (!state.lastKnownVtxos.has(key)) {
|
|
361
|
+
newVtxos.push(vtxo);
|
|
362
|
+
state.lastKnownVtxos.set(key, vtxo);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Find spent VTXOs and remove them from the contract's state
|
|
366
|
+
const spentVtxos = [];
|
|
367
|
+
for (const [key, vtxo] of state.lastKnownVtxos) {
|
|
368
|
+
if (!currentKeys.has(key)) {
|
|
369
|
+
spentVtxos.push(vtxo);
|
|
370
|
+
state.lastKnownVtxos.delete(key);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Emit events
|
|
374
|
+
if (newVtxos.length > 0) {
|
|
375
|
+
this.emitVtxoEvent(contractScript, newVtxos, "vtxo_received", now);
|
|
376
|
+
}
|
|
377
|
+
if (spentVtxos.length > 0) {
|
|
378
|
+
// Note: We can't distinguish spent vs swept from polling alone
|
|
379
|
+
// The subscription provides more accurate event types
|
|
380
|
+
this.emitVtxoEvent(contractScript, spentVtxos, "vtxo_spent", now);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
console.error("ContractWatcher poll failed:", error);
|
|
386
|
+
// Don't throw - polling failures shouldn't crash the watcher
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async tryUpdateSubscription() {
|
|
390
|
+
try {
|
|
391
|
+
await this.updateSubscription();
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
// nothing, the connection will be retried later
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Update the subscription with scripts that should be watched.
|
|
399
|
+
*
|
|
400
|
+
* Watches both active contracts and contracts with VTXOs.
|
|
401
|
+
*/
|
|
402
|
+
async updateSubscription() {
|
|
403
|
+
const scriptsToWatch = this.getScriptsToWatch();
|
|
404
|
+
if (scriptsToWatch.length === 0) {
|
|
405
|
+
if (this.subscriptionId) {
|
|
406
|
+
try {
|
|
407
|
+
await this.config.indexerProvider.unsubscribeForScripts(this.subscriptionId);
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// Ignore
|
|
411
|
+
}
|
|
412
|
+
this.subscriptionId = undefined;
|
|
413
|
+
}
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
this.subscriptionId =
|
|
417
|
+
await this.config.indexerProvider.subscribeForScripts(scriptsToWatch, this.subscriptionId);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Main listening loop for subscription events.
|
|
421
|
+
*/
|
|
422
|
+
async listenLoop() {
|
|
423
|
+
if (!this.subscriptionId || !this.abortController || !this.isWatching) {
|
|
424
|
+
if (this.isWatching) {
|
|
425
|
+
this.connectionState = "disconnected";
|
|
426
|
+
this.scheduleReconnect();
|
|
427
|
+
}
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const subscription = this.config.indexerProvider.getSubscription(this.subscriptionId, this.abortController.signal);
|
|
431
|
+
for await (const update of subscription) {
|
|
432
|
+
if (!this.isWatching)
|
|
433
|
+
break;
|
|
434
|
+
this.handleSubscriptionUpdate(update);
|
|
435
|
+
}
|
|
436
|
+
// Stream ended normally - reconnect if still watching
|
|
437
|
+
if (this.isWatching) {
|
|
438
|
+
this.connectionState = "disconnected";
|
|
439
|
+
this.scheduleReconnect();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Handle a subscription update.
|
|
444
|
+
*/
|
|
445
|
+
handleSubscriptionUpdate(update) {
|
|
446
|
+
if (!this.eventCallback)
|
|
447
|
+
return;
|
|
448
|
+
const timestamp = Date.now();
|
|
449
|
+
const scripts = update.scripts || [];
|
|
450
|
+
if (update.newVtxos?.length) {
|
|
451
|
+
this.processSubscriptionVtxos(update.newVtxos, scripts, "vtxo_received", timestamp);
|
|
452
|
+
}
|
|
453
|
+
if (update.spentVtxos?.length) {
|
|
454
|
+
this.processSubscriptionVtxos(update.spentVtxos, scripts, "vtxo_spent", timestamp);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Process VTXOs from subscription and route to correct contracts.
|
|
459
|
+
* Uses the scripts from the subscription response to determine contract ownership.
|
|
460
|
+
*/
|
|
461
|
+
processSubscriptionVtxos(vtxos, scripts, eventType, timestamp) {
|
|
462
|
+
// If we have exactly one script, all VTXOs belong to that contract
|
|
463
|
+
// Otherwise, we can't reliably determine ownership without script in VirtualCoin
|
|
464
|
+
if (scripts.length === 1) {
|
|
465
|
+
const contractScript = scripts[0];
|
|
466
|
+
if (contractScript) {
|
|
467
|
+
// Update tracking
|
|
468
|
+
const state = this.contracts.get(contractScript);
|
|
469
|
+
if (state) {
|
|
470
|
+
for (const vtxo of vtxos) {
|
|
471
|
+
const key = `${vtxo.txid}:${vtxo.vout}`;
|
|
472
|
+
if (eventType === "vtxo_received") {
|
|
473
|
+
state.lastKnownVtxos.set(key, vtxo);
|
|
474
|
+
}
|
|
475
|
+
else if (eventType === "vtxo_spent") {
|
|
476
|
+
state.lastKnownVtxos.delete(key);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
this.emitVtxoEvent(contractScript, vtxos, eventType, timestamp);
|
|
481
|
+
}
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
// Multiple scripts - assign VTXOs to all matching contracts
|
|
485
|
+
// This is a limitation: we can't know which VTXO belongs to which script
|
|
486
|
+
// In practice, subscription events usually come with a single script context
|
|
487
|
+
for (const script of scripts) {
|
|
488
|
+
const contractScript = script;
|
|
489
|
+
if (contractScript) {
|
|
490
|
+
const state = this.contracts.get(contractScript);
|
|
491
|
+
if (state) {
|
|
492
|
+
for (const vtxo of vtxos) {
|
|
493
|
+
const key = `${vtxo.txid}:${vtxo.vout}`;
|
|
494
|
+
if (eventType === "vtxo_received") {
|
|
495
|
+
state.lastKnownVtxos.set(key, vtxo);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
state.lastKnownVtxos.delete(key);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
this.emitVtxoEvent(contractScript, vtxos, eventType, timestamp);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Emit a VTXO event for a contract.
|
|
508
|
+
*/
|
|
509
|
+
emitVtxoEvent(contractScript, vtxos, eventType, timestamp) {
|
|
510
|
+
if (!this.eventCallback)
|
|
511
|
+
return;
|
|
512
|
+
const state = this.contracts.get(contractScript);
|
|
513
|
+
// ensure we check somehow regularly
|
|
514
|
+
this.checkExpiredContracts();
|
|
515
|
+
switch (eventType) {
|
|
516
|
+
case "vtxo_received":
|
|
517
|
+
if (!state)
|
|
518
|
+
return;
|
|
519
|
+
this.eventCallback({
|
|
520
|
+
type: "vtxo_received",
|
|
521
|
+
vtxos: vtxos.map((v) => ({
|
|
522
|
+
...v,
|
|
523
|
+
contractScript,
|
|
524
|
+
// These fields may not be available from basic VirtualCoin
|
|
525
|
+
forfeitTapLeafScript: undefined,
|
|
526
|
+
intentTapLeafScript: undefined,
|
|
527
|
+
tapTree: undefined,
|
|
528
|
+
})),
|
|
529
|
+
contractScript,
|
|
530
|
+
contract: state.contract,
|
|
531
|
+
timestamp,
|
|
532
|
+
});
|
|
533
|
+
return;
|
|
534
|
+
case "vtxo_spent":
|
|
535
|
+
if (!state)
|
|
536
|
+
return;
|
|
537
|
+
this.eventCallback({
|
|
538
|
+
type: "vtxo_spent",
|
|
539
|
+
vtxos: vtxos.map((v) => ({
|
|
540
|
+
...v,
|
|
541
|
+
contractScript,
|
|
542
|
+
// These fields may not be available from basic VirtualCoin
|
|
543
|
+
forfeitTapLeafScript: undefined,
|
|
544
|
+
intentTapLeafScript: undefined,
|
|
545
|
+
tapTree: undefined,
|
|
546
|
+
})),
|
|
547
|
+
contractScript,
|
|
548
|
+
contract: state.contract,
|
|
549
|
+
timestamp,
|
|
550
|
+
});
|
|
551
|
+
return;
|
|
552
|
+
case "contract_expired":
|
|
553
|
+
if (!state)
|
|
554
|
+
return;
|
|
555
|
+
this.eventCallback({
|
|
556
|
+
type: "contract_expired",
|
|
557
|
+
contractScript,
|
|
558
|
+
contract: state.contract,
|
|
559
|
+
timestamp,
|
|
560
|
+
});
|
|
561
|
+
return;
|
|
562
|
+
default:
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
exports.ContractWatcher = ContractWatcher;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DefaultContractHandler = void 0;
|
|
4
|
+
const base_1 = require("@scure/base");
|
|
5
|
+
const default_1 = require("../../script/default");
|
|
6
|
+
const helpers_1 = require("./helpers");
|
|
7
|
+
/**
|
|
8
|
+
* Handler for default wallet VTXOs.
|
|
9
|
+
*
|
|
10
|
+
* Default contracts use the standard forfeit + exit tapscript:
|
|
11
|
+
* - forfeit: (Alice + Server) multisig for collaborative spending
|
|
12
|
+
* - exit: (Alice) + CSV timelock for unilateral exit
|
|
13
|
+
*/
|
|
14
|
+
exports.DefaultContractHandler = {
|
|
15
|
+
type: "default",
|
|
16
|
+
createScript(params) {
|
|
17
|
+
const typed = this.deserializeParams(params);
|
|
18
|
+
return new default_1.DefaultVtxo.Script(typed);
|
|
19
|
+
},
|
|
20
|
+
serializeParams(params) {
|
|
21
|
+
return {
|
|
22
|
+
pubKey: base_1.hex.encode(params.pubKey),
|
|
23
|
+
serverPubKey: base_1.hex.encode(params.serverPubKey),
|
|
24
|
+
csvTimelock: (0, helpers_1.timelockToSequence)(params.csvTimelock).toString(),
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
deserializeParams(params) {
|
|
28
|
+
const csvTimelock = params.csvTimelock
|
|
29
|
+
? (0, helpers_1.sequenceToTimelock)(Number(params.csvTimelock))
|
|
30
|
+
: default_1.DefaultVtxo.Script.DEFAULT_TIMELOCK;
|
|
31
|
+
return {
|
|
32
|
+
pubKey: base_1.hex.decode(params.pubKey),
|
|
33
|
+
serverPubKey: base_1.hex.decode(params.serverPubKey),
|
|
34
|
+
csvTimelock,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
selectPath(script, contract, context) {
|
|
38
|
+
if (context.collaborative) {
|
|
39
|
+
// Use forfeit path for collaborative spending
|
|
40
|
+
return { leaf: script.forfeit() };
|
|
41
|
+
}
|
|
42
|
+
// Use exit path for unilateral exit (only if CSV is satisfied)
|
|
43
|
+
const sequence = contract.params.csvTimelock
|
|
44
|
+
? Number(contract.params.csvTimelock)
|
|
45
|
+
: undefined;
|
|
46
|
+
if (!(0, helpers_1.isCsvSpendable)(context, sequence)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
leaf: script.exit(),
|
|
51
|
+
sequence,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
getAllSpendingPaths(script, contract, context) {
|
|
55
|
+
const paths = [];
|
|
56
|
+
// Forfeit path available with server cooperation
|
|
57
|
+
if (context.collaborative) {
|
|
58
|
+
paths.push({ leaf: script.forfeit() });
|
|
59
|
+
}
|
|
60
|
+
// Exit path always possible (CSV checked at tx time)
|
|
61
|
+
const exitPath = { leaf: script.exit() };
|
|
62
|
+
if (contract.params.csvTimelock) {
|
|
63
|
+
exitPath.sequence = Number(contract.params.csvTimelock);
|
|
64
|
+
}
|
|
65
|
+
paths.push(exitPath);
|
|
66
|
+
return paths;
|
|
67
|
+
},
|
|
68
|
+
getSpendablePaths(script, contract, context) {
|
|
69
|
+
const paths = [];
|
|
70
|
+
if (context.collaborative) {
|
|
71
|
+
paths.push({ leaf: script.forfeit() });
|
|
72
|
+
}
|
|
73
|
+
const exitSequence = contract.params.csvTimelock
|
|
74
|
+
? Number(contract.params.csvTimelock)
|
|
75
|
+
: undefined;
|
|
76
|
+
if ((0, helpers_1.isCsvSpendable)(context, exitSequence)) {
|
|
77
|
+
const exitPath = { leaf: script.exit() };
|
|
78
|
+
if (exitSequence !== undefined) {
|
|
79
|
+
exitPath.sequence = exitSequence;
|
|
80
|
+
}
|
|
81
|
+
paths.push(exitPath);
|
|
82
|
+
}
|
|
83
|
+
return paths;
|
|
84
|
+
},
|
|
85
|
+
};
|