@arkade-os/sdk 0.2.3 → 0.3.0-alpha.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 (81) hide show
  1. package/README.md +114 -43
  2. package/dist/cjs/adapters/asyncStorage.js +5 -0
  3. package/dist/cjs/adapters/fileSystem.js +5 -0
  4. package/dist/cjs/adapters/indexedDB.js +5 -0
  5. package/dist/cjs/adapters/localStorage.js +5 -0
  6. package/dist/cjs/bip322/index.js +2 -2
  7. package/dist/cjs/identity/index.js +15 -0
  8. package/dist/cjs/identity/singleKey.js +20 -1
  9. package/dist/cjs/index.js +5 -3
  10. package/dist/cjs/musig2/keys.js +6 -6
  11. package/dist/cjs/musig2/sign.js +5 -5
  12. package/dist/cjs/repositories/contractRepository.js +130 -0
  13. package/dist/cjs/repositories/index.js +18 -0
  14. package/dist/cjs/repositories/walletRepository.js +136 -0
  15. package/dist/cjs/storage/asyncStorage.js +47 -0
  16. package/dist/cjs/storage/fileSystem.js +138 -0
  17. package/dist/cjs/storage/inMemory.js +21 -0
  18. package/dist/cjs/storage/indexedDB.js +97 -0
  19. package/dist/cjs/storage/localStorage.js +48 -0
  20. package/dist/cjs/tree/signingSession.js +4 -4
  21. package/dist/cjs/wallet/onchain.js +12 -6
  22. package/dist/cjs/wallet/serviceWorker/request.js +4 -14
  23. package/dist/cjs/wallet/serviceWorker/response.js +0 -13
  24. package/dist/cjs/wallet/serviceWorker/wallet.js +124 -130
  25. package/dist/cjs/wallet/serviceWorker/worker.js +84 -53
  26. package/dist/cjs/wallet/wallet.js +21 -4
  27. package/dist/esm/adapters/asyncStorage.js +1 -0
  28. package/dist/esm/adapters/fileSystem.js +1 -0
  29. package/dist/esm/adapters/indexedDB.js +1 -0
  30. package/dist/esm/adapters/localStorage.js +1 -0
  31. package/dist/esm/bip322/index.js +1 -1
  32. package/dist/esm/identity/index.js +1 -1
  33. package/dist/esm/identity/singleKey.js +21 -2
  34. package/dist/esm/index.js +4 -3
  35. package/dist/esm/musig2/keys.js +6 -6
  36. package/dist/esm/musig2/sign.js +4 -4
  37. package/dist/esm/repositories/contractRepository.js +126 -0
  38. package/dist/esm/repositories/index.js +2 -0
  39. package/dist/esm/repositories/walletRepository.js +132 -0
  40. package/dist/esm/storage/asyncStorage.js +43 -0
  41. package/dist/esm/storage/fileSystem.js +101 -0
  42. package/dist/esm/storage/inMemory.js +17 -0
  43. package/dist/esm/storage/indexedDB.js +93 -0
  44. package/dist/esm/storage/localStorage.js +44 -0
  45. package/dist/esm/tree/signingSession.js +1 -1
  46. package/dist/esm/wallet/onchain.js +12 -6
  47. package/dist/esm/wallet/serviceWorker/request.js +4 -14
  48. package/dist/esm/wallet/serviceWorker/response.js +0 -13
  49. package/dist/esm/wallet/serviceWorker/wallet.js +125 -131
  50. package/dist/esm/wallet/serviceWorker/worker.js +85 -54
  51. package/dist/esm/wallet/wallet.js +21 -4
  52. package/dist/types/adapters/asyncStorage.d.ts +2 -0
  53. package/dist/types/adapters/fileSystem.d.ts +2 -0
  54. package/dist/types/adapters/indexedDB.d.ts +2 -0
  55. package/dist/types/adapters/localStorage.d.ts +2 -0
  56. package/dist/types/identity/index.d.ts +3 -1
  57. package/dist/types/identity/singleKey.d.ts +12 -1
  58. package/dist/types/index.d.ts +4 -4
  59. package/dist/types/repositories/contractRepository.d.ts +20 -0
  60. package/dist/types/repositories/index.d.ts +2 -0
  61. package/dist/types/repositories/walletRepository.d.ts +38 -0
  62. package/dist/types/storage/asyncStorage.d.ts +9 -0
  63. package/dist/types/storage/fileSystem.d.ts +11 -0
  64. package/dist/types/storage/inMemory.d.ts +8 -0
  65. package/dist/types/storage/index.d.ts +6 -0
  66. package/dist/types/storage/indexedDB.d.ts +12 -0
  67. package/dist/types/storage/localStorage.d.ts +8 -0
  68. package/dist/types/wallet/index.d.ts +3 -0
  69. package/dist/types/wallet/onchain.d.ts +3 -2
  70. package/dist/types/wallet/serviceWorker/request.d.ts +1 -7
  71. package/dist/types/wallet/serviceWorker/response.d.ts +1 -8
  72. package/dist/types/wallet/serviceWorker/wallet.d.ts +67 -21
  73. package/dist/types/wallet/serviceWorker/worker.d.ts +16 -4
  74. package/dist/types/wallet/wallet.d.ts +4 -0
  75. package/package.json +38 -14
  76. package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +0 -185
  77. package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +0 -181
  78. package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +0 -20
  79. package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +0 -14
  80. /package/dist/cjs/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
  81. /package/dist/esm/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
@@ -1,9 +1,13 @@
1
- import { IWallet, WalletBalance, SendBitcoinParams, SettleParams, ArkTransaction, WalletConfig, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter } from "..";
1
+ import { IWallet, WalletBalance, SendBitcoinParams, SettleParams, ArkTransaction, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter } from "..";
2
2
  import { Response } from "./response";
3
3
  import { SettlementEvent } from "../../providers/ark";
4
4
  import { Identity } from "../../identity";
5
- import { SignerSession } from "../../tree/signingSession";
6
- import { Transaction } from "@scure/btc-signer";
5
+ import { StorageAdapter } from "../../storage";
6
+ import { WalletRepository } from "../../repositories/walletRepository";
7
+ import { ContractRepository } from "../../repositories/contractRepository";
8
+ export type PrivateKeyIdentity = Identity & {
9
+ toHex(): string;
10
+ };
7
11
  /**
8
12
  * Service Worker-based wallet implementation for browser environments.
9
13
  *
@@ -14,12 +18,21 @@ import { Transaction } from "@scure/btc-signer";
14
18
  *
15
19
  * @example
16
20
  * ```typescript
17
- * // Create and initialize the service worker wallet
21
+ * // SIMPLE: Recommended approach
22
+ * const identity = SingleKey.fromHex('your_private_key_hex');
23
+ * const wallet = await ServiceWorkerWallet.setup({
24
+ * serviceWorkerPath: '/service-worker.js',
25
+ * arkServerUrl: 'https://mutinynet.arkade.sh',
26
+ * identity
27
+ * });
28
+ *
29
+ * // ADVANCED: Manual setup with service worker control
18
30
  * const serviceWorker = await setupServiceWorker("/service-worker.js");
19
- * const wallet = new ServiceWorkerWallet(serviceWorker);
20
- * await wallet.init({
21
- * privateKey: 'your_private_key_hex',
22
- * arkServerUrl: 'https://ark.example.com'
31
+ * const identity = SingleKey.fromHex('your_private_key_hex');
32
+ * const wallet = await ServiceWorkerWallet.create({
33
+ * serviceWorker,
34
+ * identity,
35
+ * arkServerUrl: 'https://mutinynet.arkade.sh'
23
36
  * });
24
37
  *
25
38
  * // Use like any other wallet
@@ -27,25 +40,58 @@ import { Transaction } from "@scure/btc-signer";
27
40
  * const balance = await wallet.getBalance();
28
41
  * ```
29
42
  */
30
- export declare class ServiceWorkerWallet implements IWallet, Identity {
43
+ interface ServiceWorkerWalletOptions {
44
+ arkServerPublicKey?: string;
45
+ arkServerUrl: string;
46
+ esploraUrl?: string;
47
+ identity: PrivateKeyIdentity;
48
+ storage?: StorageAdapter;
49
+ }
50
+ export type ServiceWorkerWalletCreateOptions = ServiceWorkerWalletOptions & {
51
+ serviceWorker: ServiceWorker;
52
+ };
53
+ export type ServiceWorkerWalletSetupOptions = ServiceWorkerWalletOptions & {
54
+ serviceWorkerPath: string;
55
+ };
56
+ export declare class ServiceWorkerWallet implements IWallet {
31
57
  readonly serviceWorker: ServiceWorker;
32
- private cachedXOnlyPublicKey;
33
- constructor(serviceWorker: ServiceWorker);
34
- getStatus(): Promise<Response.WalletStatus["status"]>;
35
- init(config: Omit<WalletConfig, "identity"> & {
36
- privateKey: string;
37
- }, failIfInitialized?: boolean): Promise<void>;
38
- clear(): Promise<void>;
58
+ readonly walletRepository: WalletRepository;
59
+ readonly contractRepository: ContractRepository;
60
+ readonly identity: Identity;
61
+ private constructor();
62
+ static create(options: ServiceWorkerWalletCreateOptions): Promise<ServiceWorkerWallet>;
63
+ /**
64
+ * Simplified setup method that handles service worker registration,
65
+ * identity creation, and wallet initialization automatically.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * // One-liner setup - handles everything automatically!
70
+ * const wallet = await ServiceWorkerWallet.setup({
71
+ * serviceWorkerPath: '/service-worker.js',
72
+ * arkServerUrl: 'https://mutinynet.arkade.sh'
73
+ * });
74
+ *
75
+ * // With custom identity
76
+ * const identity = SingleKey.fromHex('your_private_key_hex');
77
+ * const wallet = await ServiceWorkerWallet.setup({
78
+ * serviceWorkerPath: '/service-worker.js',
79
+ * arkServerUrl: 'https://mutinynet.arkade.sh',
80
+ * identity
81
+ * });
82
+ * ```
83
+ */
84
+ static setup(options: ServiceWorkerWalletSetupOptions): Promise<ServiceWorkerWallet>;
39
85
  private sendMessage;
86
+ clear(): Promise<void>;
40
87
  getAddress(): Promise<string>;
41
88
  getBoardingAddress(): Promise<string>;
42
89
  getBalance(): Promise<WalletBalance>;
43
- getVtxos(filter?: GetVtxosFilter): Promise<ExtendedVirtualCoin[]>;
44
90
  getBoardingUtxos(): Promise<ExtendedCoin[]>;
91
+ getStatus(): Promise<Response.WalletStatus["status"]>;
92
+ getTransactionHistory(): Promise<ArkTransaction[]>;
93
+ getVtxos(filter?: GetVtxosFilter): Promise<ExtendedVirtualCoin[]>;
45
94
  sendBitcoin(params: SendBitcoinParams): Promise<string>;
46
95
  settle(params?: SettleParams, callback?: (event: SettlementEvent) => void): Promise<string>;
47
- getTransactionHistory(): Promise<ArkTransaction[]>;
48
- xOnlyPublicKey(): Uint8Array;
49
- signerSession(): SignerSession;
50
- sign(tx: Transaction, inputIndexes?: number[]): Promise<Transaction>;
51
96
  }
97
+ export {};
@@ -1,16 +1,28 @@
1
- import { VtxoRepository } from "./db/vtxo";
2
1
  /**
3
2
  * Worker is a class letting to interact with ServiceWorkerWallet from the client
4
3
  * it aims to be run in a service worker context
5
4
  */
6
5
  export declare class Worker {
7
- private readonly vtxoRepository;
8
6
  private readonly messageCallback;
9
7
  private wallet;
10
8
  private arkProvider;
11
9
  private indexerProvider;
12
10
  private vtxoSubscription;
13
- constructor(vtxoRepository?: VtxoRepository, messageCallback?: (message: ExtendableMessageEvent) => void);
11
+ private walletRepository;
12
+ private storage;
13
+ constructor(messageCallback?: (message: ExtendableMessageEvent) => void);
14
+ /**
15
+ * Get spendable vtxos for the current wallet address
16
+ */
17
+ private getSpendableVtxos;
18
+ /**
19
+ * Get swept vtxos for the current wallet address
20
+ */
21
+ private getSweptVtxos;
22
+ /**
23
+ * Get all vtxos categorized by type
24
+ */
25
+ private getAllVtxos;
14
26
  start(withServiceWorkerUpdate?: boolean): Promise<void>;
15
27
  clear(): Promise<void>;
16
28
  reload(): Promise<void>;
@@ -27,6 +39,6 @@ export declare class Worker {
27
39
  private handleGetBoardingUtxos;
28
40
  private handleGetTransactionHistory;
29
41
  private handleGetStatus;
30
- private handleSign;
31
42
  private handleMessage;
43
+ private sendMessageToAllClients;
32
44
  }
@@ -8,6 +8,8 @@ import { ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter
8
8
  import { Bytes } from "@scure/btc-signer/utils";
9
9
  import { CSVMultisigTapscript } from "../script/tapscript";
10
10
  import { IndexerProvider } from "../providers/indexer";
11
+ import { WalletRepository } from "../repositories/walletRepository";
12
+ import { ContractRepository } from "../repositories/contractRepository";
11
13
  export type IncomingFunds = {
12
14
  type: "utxo";
13
15
  coins: Coin[];
@@ -54,6 +56,8 @@ export declare class Wallet implements IWallet {
54
56
  readonly forfeitOutputScript: Bytes;
55
57
  readonly dustAmount: bigint;
56
58
  static MIN_FEE_RATE: number;
59
+ readonly walletRepository: WalletRepository;
60
+ readonly contractRepository: ContractRepository;
57
61
  private constructor();
58
62
  static create(config: WalletConfig): Promise<Wallet>;
59
63
  get arkAddress(): ArkAddress;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.2.3",
3
+ "version": "0.3.0-alpha.0",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -13,6 +13,30 @@
13
13
  "import": "./dist/esm/index.js",
14
14
  "require": "./dist/cjs/index.js",
15
15
  "default": "./dist/esm/index.js"
16
+ },
17
+ "./adapters/localStorage": {
18
+ "types": "./dist/types/adapters/localStorage.d.ts",
19
+ "import": "./dist/esm/adapters/localStorage.js",
20
+ "require": "./dist/cjs/adapters/localStorage.js",
21
+ "default": "./dist/esm/adapters/localStorage.js"
22
+ },
23
+ "./adapters/fileSystem": {
24
+ "types": "./dist/types/adapters/fileSystem.d.ts",
25
+ "import": "./dist/esm/adapters/fileSystem.js",
26
+ "require": "./dist/cjs/adapters/fileSystem.js",
27
+ "default": "./dist/esm/adapters/fileSystem.js"
28
+ },
29
+ "./adapters/indexedDB": {
30
+ "types": "./dist/types/adapters/indexedDB.d.ts",
31
+ "import": "./dist/esm/adapters/indexedDB.js",
32
+ "require": "./dist/cjs/adapters/indexedDB.js",
33
+ "default": "./dist/esm/adapters/indexedDB.js"
34
+ },
35
+ "./adapters/asyncStorage": {
36
+ "types": "./dist/types/adapters/asyncStorage.d.ts",
37
+ "import": "./dist/esm/adapters/asyncStorage.js",
38
+ "require": "./dist/cjs/adapters/asyncStorage.js",
39
+ "default": "./dist/esm/adapters/asyncStorage.js"
16
40
  }
17
41
  },
18
42
  "files": [
@@ -23,24 +47,24 @@
23
47
  "registry": "https://registry.npmjs.org/"
24
48
  },
25
49
  "dependencies": {
26
- "@noble/curves": "1.9.1",
27
- "@noble/hashes": "1.8.0",
28
- "@noble/secp256k1": "2.2.3",
29
- "@scure/base": "1.2.6",
50
+ "@noble/curves": "2.0.0",
51
+ "@noble/hashes": "2.0.0",
52
+ "@noble/secp256k1": "3.0.0",
53
+ "@scure/base": "2.0.0",
30
54
  "@scure/btc-signer": "1.8.1",
31
55
  "bip68": "1.0.4"
32
56
  },
33
57
  "devDependencies": {
34
- "rimraf": "^5.0.0",
35
- "@types/node": "22.10.2",
36
- "@vitest/coverage-v8": "2.1.9",
37
- "esbuild": "^0.20.1",
38
- "glob": "11.0.1",
58
+ "@types/node": "24.3.1",
59
+ "@vitest/coverage-v8": "3.2.4",
60
+ "esbuild": "^0.25.9",
61
+ "glob": "11.0.3",
39
62
  "husky": "9.1.7",
40
- "prettier": "3.4.2",
41
- "typedoc": "^0.27.1",
42
- "typescript": "5.7.2",
43
- "vitest": "2.1.9"
63
+ "prettier": "3.6.2",
64
+ "rimraf": "^6.0.1",
65
+ "typedoc": "^0.28.12",
66
+ "typescript": "5.9.2",
67
+ "vitest": "3.2.4"
44
68
  },
45
69
  "keywords": [
46
70
  "bitcoin",
@@ -1,185 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IndexedDBVtxoRepository = void 0;
4
- class IndexedDBVtxoRepository {
5
- constructor() {
6
- this.db = null;
7
- }
8
- static delete() {
9
- return new Promise((resolve, reject) => {
10
- try {
11
- const request = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME);
12
- request.onblocked = () => {
13
- // If blocked, wait a bit and try again
14
- setTimeout(() => {
15
- const retryRequest = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME);
16
- retryRequest.onsuccess = () => resolve();
17
- retryRequest.onerror = () => reject(retryRequest.error ||
18
- new Error("Failed to delete database"));
19
- }, 100);
20
- };
21
- request.onsuccess = () => {
22
- resolve();
23
- };
24
- request.onerror = () => {
25
- reject(request.error || new Error("Failed to delete database"));
26
- };
27
- }
28
- catch (error) {
29
- reject(error instanceof Error
30
- ? error
31
- : new Error("Failed to delete database"));
32
- }
33
- });
34
- }
35
- async close() {
36
- if (this.db) {
37
- this.db.close();
38
- this.db = null;
39
- }
40
- }
41
- async open() {
42
- return new Promise((resolve, reject) => {
43
- const request = indexedDB.open(IndexedDBVtxoRepository.DB_NAME, IndexedDBVtxoRepository.DB_VERSION);
44
- request.onerror = () => {
45
- reject(request.error);
46
- };
47
- request.onsuccess = () => {
48
- this.db = request.result;
49
- resolve();
50
- };
51
- request.onupgradeneeded = (event) => {
52
- const db = event.target.result;
53
- if (!db.objectStoreNames.contains(IndexedDBVtxoRepository.STORE_NAME)) {
54
- const store = db.createObjectStore(IndexedDBVtxoRepository.STORE_NAME, {
55
- keyPath: ["txid", "vout"],
56
- });
57
- store.createIndex("state", "virtualStatus.state", {
58
- unique: false,
59
- });
60
- store.createIndex("spentBy", "spentBy", {
61
- unique: false,
62
- });
63
- }
64
- };
65
- });
66
- }
67
- async addOrUpdate(vtxos) {
68
- if (!this.db) {
69
- throw new Error("Database not opened");
70
- }
71
- return new Promise((resolve, reject) => {
72
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite");
73
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
74
- const requests = vtxos.map((vtxo) => {
75
- return new Promise((resolveRequest, rejectRequest) => {
76
- const request = store.put(vtxo);
77
- request.onsuccess = () => resolveRequest();
78
- request.onerror = () => rejectRequest(request.error);
79
- });
80
- });
81
- Promise.all(requests)
82
- .then(() => resolve())
83
- .catch(reject);
84
- });
85
- }
86
- async deleteAll() {
87
- if (!this.db) {
88
- throw new Error("Database not opened");
89
- }
90
- return new Promise((resolve, reject) => {
91
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite");
92
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
93
- const request = store.clear();
94
- request.onsuccess = () => resolve();
95
- request.onerror = () => reject(request.error);
96
- });
97
- }
98
- async getSpendableVtxos() {
99
- if (!this.db) {
100
- throw new Error("Database not opened");
101
- }
102
- return new Promise((resolve, reject) => {
103
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
104
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
105
- const spentByIndex = store.index("spentBy");
106
- // Get vtxos where spentBy is empty string
107
- const request = spentByIndex.getAll(IDBKeyRange.only(""));
108
- request.onsuccess = () => {
109
- resolve(request.result);
110
- };
111
- request.onerror = () => reject(request.error);
112
- });
113
- }
114
- async getSweptVtxos() {
115
- if (!this.db) {
116
- throw new Error("Database not opened");
117
- }
118
- return new Promise((resolve, reject) => {
119
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
120
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
121
- const stateIndex = store.index("state");
122
- // Get vtxos where state is "swept"
123
- const request = stateIndex.getAll(IDBKeyRange.only("swept"));
124
- request.onsuccess = () => {
125
- resolve(request.result);
126
- };
127
- request.onerror = () => reject(request.error);
128
- });
129
- }
130
- async getSpentVtxos() {
131
- if (!this.db) {
132
- throw new Error("Database not opened");
133
- }
134
- return new Promise((resolve, reject) => {
135
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
136
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
137
- const spentByIndex = store.index("spentBy");
138
- // Get vtxos where spentBy is not empty string
139
- const request = spentByIndex.getAll(IDBKeyRange.lowerBound("", true));
140
- request.onsuccess = () => {
141
- resolve(request.result);
142
- };
143
- request.onerror = () => reject(request.error);
144
- });
145
- }
146
- async getAllVtxos() {
147
- if (!this.db) {
148
- throw new Error("Database not opened");
149
- }
150
- return new Promise((resolve, reject) => {
151
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
152
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
153
- const spentByIndex = store.index("spentBy");
154
- // Get vtxos where spentBy is empty string
155
- const spendableRequest = spentByIndex.getAll(IDBKeyRange.only(""));
156
- // Get all vtxos where spentBy is populated
157
- const spentRequest = spentByIndex.getAll(IDBKeyRange.lowerBound("", true));
158
- Promise.all([
159
- new Promise((resolveSpendable, rejectSpendable) => {
160
- spendableRequest.onsuccess = () => {
161
- resolveSpendable(spendableRequest.result);
162
- };
163
- spendableRequest.onerror = () => rejectSpendable(spendableRequest.error);
164
- }),
165
- new Promise((resolveSpent, rejectSpent) => {
166
- spentRequest.onsuccess = () => {
167
- resolveSpent(spentRequest.result);
168
- };
169
- spentRequest.onerror = () => rejectSpent(spentRequest.error);
170
- }),
171
- ])
172
- .then(([spendableVtxos, spentVtxos]) => {
173
- resolve({
174
- spendable: spendableVtxos,
175
- spent: spentVtxos,
176
- });
177
- })
178
- .catch(reject);
179
- });
180
- }
181
- }
182
- exports.IndexedDBVtxoRepository = IndexedDBVtxoRepository;
183
- IndexedDBVtxoRepository.DB_NAME = "wallet-db";
184
- IndexedDBVtxoRepository.STORE_NAME = "vtxos";
185
- IndexedDBVtxoRepository.DB_VERSION = 1;
@@ -1,181 +0,0 @@
1
- export class IndexedDBVtxoRepository {
2
- constructor() {
3
- this.db = null;
4
- }
5
- static delete() {
6
- return new Promise((resolve, reject) => {
7
- try {
8
- const request = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME);
9
- request.onblocked = () => {
10
- // If blocked, wait a bit and try again
11
- setTimeout(() => {
12
- const retryRequest = indexedDB.deleteDatabase(IndexedDBVtxoRepository.DB_NAME);
13
- retryRequest.onsuccess = () => resolve();
14
- retryRequest.onerror = () => reject(retryRequest.error ||
15
- new Error("Failed to delete database"));
16
- }, 100);
17
- };
18
- request.onsuccess = () => {
19
- resolve();
20
- };
21
- request.onerror = () => {
22
- reject(request.error || new Error("Failed to delete database"));
23
- };
24
- }
25
- catch (error) {
26
- reject(error instanceof Error
27
- ? error
28
- : new Error("Failed to delete database"));
29
- }
30
- });
31
- }
32
- async close() {
33
- if (this.db) {
34
- this.db.close();
35
- this.db = null;
36
- }
37
- }
38
- async open() {
39
- return new Promise((resolve, reject) => {
40
- const request = indexedDB.open(IndexedDBVtxoRepository.DB_NAME, IndexedDBVtxoRepository.DB_VERSION);
41
- request.onerror = () => {
42
- reject(request.error);
43
- };
44
- request.onsuccess = () => {
45
- this.db = request.result;
46
- resolve();
47
- };
48
- request.onupgradeneeded = (event) => {
49
- const db = event.target.result;
50
- if (!db.objectStoreNames.contains(IndexedDBVtxoRepository.STORE_NAME)) {
51
- const store = db.createObjectStore(IndexedDBVtxoRepository.STORE_NAME, {
52
- keyPath: ["txid", "vout"],
53
- });
54
- store.createIndex("state", "virtualStatus.state", {
55
- unique: false,
56
- });
57
- store.createIndex("spentBy", "spentBy", {
58
- unique: false,
59
- });
60
- }
61
- };
62
- });
63
- }
64
- async addOrUpdate(vtxos) {
65
- if (!this.db) {
66
- throw new Error("Database not opened");
67
- }
68
- return new Promise((resolve, reject) => {
69
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite");
70
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
71
- const requests = vtxos.map((vtxo) => {
72
- return new Promise((resolveRequest, rejectRequest) => {
73
- const request = store.put(vtxo);
74
- request.onsuccess = () => resolveRequest();
75
- request.onerror = () => rejectRequest(request.error);
76
- });
77
- });
78
- Promise.all(requests)
79
- .then(() => resolve())
80
- .catch(reject);
81
- });
82
- }
83
- async deleteAll() {
84
- if (!this.db) {
85
- throw new Error("Database not opened");
86
- }
87
- return new Promise((resolve, reject) => {
88
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readwrite");
89
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
90
- const request = store.clear();
91
- request.onsuccess = () => resolve();
92
- request.onerror = () => reject(request.error);
93
- });
94
- }
95
- async getSpendableVtxos() {
96
- if (!this.db) {
97
- throw new Error("Database not opened");
98
- }
99
- return new Promise((resolve, reject) => {
100
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
101
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
102
- const spentByIndex = store.index("spentBy");
103
- // Get vtxos where spentBy is empty string
104
- const request = spentByIndex.getAll(IDBKeyRange.only(""));
105
- request.onsuccess = () => {
106
- resolve(request.result);
107
- };
108
- request.onerror = () => reject(request.error);
109
- });
110
- }
111
- async getSweptVtxos() {
112
- if (!this.db) {
113
- throw new Error("Database not opened");
114
- }
115
- return new Promise((resolve, reject) => {
116
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
117
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
118
- const stateIndex = store.index("state");
119
- // Get vtxos where state is "swept"
120
- const request = stateIndex.getAll(IDBKeyRange.only("swept"));
121
- request.onsuccess = () => {
122
- resolve(request.result);
123
- };
124
- request.onerror = () => reject(request.error);
125
- });
126
- }
127
- async getSpentVtxos() {
128
- if (!this.db) {
129
- throw new Error("Database not opened");
130
- }
131
- return new Promise((resolve, reject) => {
132
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
133
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
134
- const spentByIndex = store.index("spentBy");
135
- // Get vtxos where spentBy is not empty string
136
- const request = spentByIndex.getAll(IDBKeyRange.lowerBound("", true));
137
- request.onsuccess = () => {
138
- resolve(request.result);
139
- };
140
- request.onerror = () => reject(request.error);
141
- });
142
- }
143
- async getAllVtxos() {
144
- if (!this.db) {
145
- throw new Error("Database not opened");
146
- }
147
- return new Promise((resolve, reject) => {
148
- const transaction = this.db.transaction(IndexedDBVtxoRepository.STORE_NAME, "readonly");
149
- const store = transaction.objectStore(IndexedDBVtxoRepository.STORE_NAME);
150
- const spentByIndex = store.index("spentBy");
151
- // Get vtxos where spentBy is empty string
152
- const spendableRequest = spentByIndex.getAll(IDBKeyRange.only(""));
153
- // Get all vtxos where spentBy is populated
154
- const spentRequest = spentByIndex.getAll(IDBKeyRange.lowerBound("", true));
155
- Promise.all([
156
- new Promise((resolveSpendable, rejectSpendable) => {
157
- spendableRequest.onsuccess = () => {
158
- resolveSpendable(spendableRequest.result);
159
- };
160
- spendableRequest.onerror = () => rejectSpendable(spendableRequest.error);
161
- }),
162
- new Promise((resolveSpent, rejectSpent) => {
163
- spentRequest.onsuccess = () => {
164
- resolveSpent(spentRequest.result);
165
- };
166
- spentRequest.onerror = () => rejectSpent(spentRequest.error);
167
- }),
168
- ])
169
- .then(([spendableVtxos, spentVtxos]) => {
170
- resolve({
171
- spendable: spendableVtxos,
172
- spent: spentVtxos,
173
- });
174
- })
175
- .catch(reject);
176
- });
177
- }
178
- }
179
- IndexedDBVtxoRepository.DB_NAME = "wallet-db";
180
- IndexedDBVtxoRepository.STORE_NAME = "vtxos";
181
- IndexedDBVtxoRepository.DB_VERSION = 1;
@@ -1,20 +0,0 @@
1
- import { VtxoRepository } from ".";
2
- import { ExtendedVirtualCoin } from "../../..";
3
- export declare class IndexedDBVtxoRepository implements VtxoRepository {
4
- static readonly DB_NAME = "wallet-db";
5
- static readonly STORE_NAME = "vtxos";
6
- static readonly DB_VERSION = 1;
7
- static delete(): Promise<void>;
8
- private db;
9
- close(): Promise<void>;
10
- open(): Promise<void>;
11
- addOrUpdate(vtxos: ExtendedVirtualCoin[]): Promise<void>;
12
- deleteAll(): Promise<void>;
13
- getSpendableVtxos(): Promise<ExtendedVirtualCoin[]>;
14
- getSweptVtxos(): Promise<ExtendedVirtualCoin[]>;
15
- getSpentVtxos(): Promise<ExtendedVirtualCoin[]>;
16
- getAllVtxos(): Promise<{
17
- spendable: ExtendedVirtualCoin[];
18
- spent: ExtendedVirtualCoin[];
19
- }>;
20
- }
@@ -1,14 +0,0 @@
1
- import { ExtendedVirtualCoin } from "../../../..";
2
- export interface VtxoRepository {
3
- addOrUpdate(vtxos: ExtendedVirtualCoin[]): Promise<void>;
4
- deleteAll(): Promise<void>;
5
- getSpendableVtxos(): Promise<ExtendedVirtualCoin[]>;
6
- getSweptVtxos(): Promise<ExtendedVirtualCoin[]>;
7
- getSpentVtxos(): Promise<ExtendedVirtualCoin[]>;
8
- getAllVtxos(): Promise<{
9
- spendable: ExtendedVirtualCoin[];
10
- spent: ExtendedVirtualCoin[];
11
- }>;
12
- close(): Promise<void>;
13
- open(): Promise<void>;
14
- }