@arkade-os/sdk 0.3.1-alpha.2 → 0.3.1-alpha.3
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/index.js +5 -1
- package/dist/cjs/providers/ark.js +18 -28
- package/dist/cjs/providers/errors.js +59 -0
- package/dist/cjs/repositories/walletRepository.js +0 -26
- package/dist/cjs/wallet/serviceWorker/response.js +32 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +3 -0
- package/dist/cjs/wallet/serviceWorker/worker.js +4 -7
- package/dist/esm/index.js +4 -1
- package/dist/esm/providers/ark.js +18 -28
- package/dist/esm/providers/errors.js +54 -0
- package/dist/esm/repositories/walletRepository.js +0 -26
- package/dist/esm/wallet/serviceWorker/response.js +32 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +3 -0
- package/dist/esm/wallet/serviceWorker/worker.js +4 -7
- package/dist/types/index.d.ts +2 -1
- package/dist/types/providers/errors.d.ts +13 -0
- package/dist/types/repositories/walletRepository.d.ts +0 -4
- package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
3
|
+
exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.networks = exports.ArkNote = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.Response = exports.Request = exports.ServiceWorkerWallet = exports.Worker = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.SingleKey = exports.Wallet = void 0;
|
|
4
|
+
exports.maybeArkError = void 0;
|
|
4
5
|
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
5
6
|
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_js_1.Transaction; } });
|
|
6
7
|
const singleKey_1 = require("./identity/singleKey");
|
|
@@ -80,3 +81,6 @@ const walletRepository_1 = require("./repositories/walletRepository");
|
|
|
80
81
|
Object.defineProperty(exports, "WalletRepositoryImpl", { enumerable: true, get: function () { return walletRepository_1.WalletRepositoryImpl; } });
|
|
81
82
|
const contractRepository_1 = require("./repositories/contractRepository");
|
|
82
83
|
Object.defineProperty(exports, "ContractRepositoryImpl", { enumerable: true, get: function () { return contractRepository_1.ContractRepositoryImpl; } });
|
|
84
|
+
const errors_1 = require("./providers/errors");
|
|
85
|
+
Object.defineProperty(exports, "ArkError", { enumerable: true, get: function () { return errors_1.ArkError; } });
|
|
86
|
+
Object.defineProperty(exports, "maybeArkError", { enumerable: true, get: function () { return errors_1.maybeArkError; } });
|
|
@@ -4,6 +4,7 @@ exports.RestArkProvider = exports.SettlementEventType = void 0;
|
|
|
4
4
|
exports.isFetchTimeoutError = isFetchTimeoutError;
|
|
5
5
|
const base_1 = require("@scure/base");
|
|
6
6
|
const utils_1 = require("./utils");
|
|
7
|
+
const errors_1 = require("./errors");
|
|
7
8
|
var SettlementEventType;
|
|
8
9
|
(function (SettlementEventType) {
|
|
9
10
|
SettlementEventType["BatchStarted"] = "batch_started";
|
|
@@ -32,7 +33,8 @@ class RestArkProvider {
|
|
|
32
33
|
const url = `${this.serverUrl}/v1/info`;
|
|
33
34
|
const response = await fetch(url);
|
|
34
35
|
if (!response.ok) {
|
|
35
|
-
|
|
36
|
+
const errorText = await response.text();
|
|
37
|
+
handleError(errorText, `Failed to get server info: ${response.statusText}`);
|
|
36
38
|
}
|
|
37
39
|
const fromServer = await response.json();
|
|
38
40
|
return {
|
|
@@ -82,16 +84,7 @@ class RestArkProvider {
|
|
|
82
84
|
});
|
|
83
85
|
if (!response.ok) {
|
|
84
86
|
const errorText = await response.text();
|
|
85
|
-
|
|
86
|
-
const grpcError = JSON.parse(errorText);
|
|
87
|
-
// gRPC errors usually have a message and code field
|
|
88
|
-
throw new Error(`Failed to submit virtual transaction: ${grpcError.message || grpcError.error || errorText}`);
|
|
89
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
90
|
-
}
|
|
91
|
-
catch (_) {
|
|
92
|
-
// If JSON parse fails, use the raw error text
|
|
93
|
-
throw new Error(`Failed to submit virtual transaction: ${errorText}`);
|
|
94
|
-
}
|
|
87
|
+
handleError(errorText, `Failed to submit virtual transaction: ${errorText}`);
|
|
95
88
|
}
|
|
96
89
|
const data = await response.json();
|
|
97
90
|
return {
|
|
@@ -114,7 +107,7 @@ class RestArkProvider {
|
|
|
114
107
|
});
|
|
115
108
|
if (!response.ok) {
|
|
116
109
|
const errorText = await response.text();
|
|
117
|
-
|
|
110
|
+
handleError(errorText, `Failed to finalize offchain transaction: ${errorText}`);
|
|
118
111
|
}
|
|
119
112
|
}
|
|
120
113
|
async registerIntent(intent) {
|
|
@@ -133,7 +126,7 @@ class RestArkProvider {
|
|
|
133
126
|
});
|
|
134
127
|
if (!response.ok) {
|
|
135
128
|
const errorText = await response.text();
|
|
136
|
-
|
|
129
|
+
handleError(errorText, `Failed to register intent: ${errorText}`);
|
|
137
130
|
}
|
|
138
131
|
const data = await response.json();
|
|
139
132
|
return data.intentId;
|
|
@@ -154,7 +147,7 @@ class RestArkProvider {
|
|
|
154
147
|
});
|
|
155
148
|
if (!response.ok) {
|
|
156
149
|
const errorText = await response.text();
|
|
157
|
-
|
|
150
|
+
handleError(errorText, `Failed to delete intent: ${errorText}`);
|
|
158
151
|
}
|
|
159
152
|
}
|
|
160
153
|
async confirmRegistration(intentId) {
|
|
@@ -170,7 +163,7 @@ class RestArkProvider {
|
|
|
170
163
|
});
|
|
171
164
|
if (!response.ok) {
|
|
172
165
|
const errorText = await response.text();
|
|
173
|
-
|
|
166
|
+
handleError(errorText, `Failed to confirm registration: ${errorText}`);
|
|
174
167
|
}
|
|
175
168
|
}
|
|
176
169
|
async submitTreeNonces(batchId, pubkey, nonces) {
|
|
@@ -188,7 +181,7 @@ class RestArkProvider {
|
|
|
188
181
|
});
|
|
189
182
|
if (!response.ok) {
|
|
190
183
|
const errorText = await response.text();
|
|
191
|
-
|
|
184
|
+
handleError(errorText, `Failed to submit tree nonces: ${errorText}`);
|
|
192
185
|
}
|
|
193
186
|
}
|
|
194
187
|
async submitTreeSignatures(batchId, pubkey, signatures) {
|
|
@@ -206,7 +199,7 @@ class RestArkProvider {
|
|
|
206
199
|
});
|
|
207
200
|
if (!response.ok) {
|
|
208
201
|
const errorText = await response.text();
|
|
209
|
-
|
|
202
|
+
handleError(errorText, `Failed to submit tree signatures: ${errorText}`);
|
|
210
203
|
}
|
|
211
204
|
}
|
|
212
205
|
async submitSignedForfeitTxs(signedForfeitTxs, signedCommitmentTx) {
|
|
@@ -222,7 +215,8 @@ class RestArkProvider {
|
|
|
222
215
|
}),
|
|
223
216
|
});
|
|
224
217
|
if (!response.ok) {
|
|
225
|
-
|
|
218
|
+
const errorText = await response.text();
|
|
219
|
+
handleError(errorText, `Failed to submit forfeit transactions: ${response.statusText}`);
|
|
226
220
|
}
|
|
227
221
|
}
|
|
228
222
|
async *getEventStream(signal, topics) {
|
|
@@ -331,16 +325,7 @@ class RestArkProvider {
|
|
|
331
325
|
});
|
|
332
326
|
if (!response.ok) {
|
|
333
327
|
const errorText = await response.text();
|
|
334
|
-
|
|
335
|
-
const grpcError = JSON.parse(errorText);
|
|
336
|
-
// gRPC errors usually have a message and code field
|
|
337
|
-
throw new Error(`Failed to get pending transactions: ${grpcError.message || grpcError.error || errorText}`);
|
|
338
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
339
|
-
}
|
|
340
|
-
catch (_) {
|
|
341
|
-
// If JSON parse fails, use the raw error text
|
|
342
|
-
throw new Error(`Failed to get pending transactions: ${errorText}`);
|
|
343
|
-
}
|
|
328
|
+
handleError(errorText, `Failed to get pending transactions: ${errorText}`);
|
|
344
329
|
}
|
|
345
330
|
const data = await response.json();
|
|
346
331
|
return data.pendingTxs;
|
|
@@ -524,3 +509,8 @@ function mapVtxo(vtxo) {
|
|
|
524
509
|
arkTxid: vtxo.arkTxid,
|
|
525
510
|
};
|
|
526
511
|
}
|
|
512
|
+
function handleError(errorText, defaultMessage) {
|
|
513
|
+
const error = new Error(errorText);
|
|
514
|
+
const arkError = (0, errors_1.maybeArkError)(error);
|
|
515
|
+
throw arkError ?? new Error(defaultMessage);
|
|
516
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ArkError = void 0;
|
|
4
|
+
exports.maybeArkError = maybeArkError;
|
|
5
|
+
class ArkError extends Error {
|
|
6
|
+
constructor(code, message, name, metadata) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.message = message;
|
|
10
|
+
this.name = name;
|
|
11
|
+
this.metadata = metadata;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.ArkError = ArkError;
|
|
15
|
+
/**
|
|
16
|
+
* Try to convert an error to an ArkError class, returning undefined if the error is not an ArkError
|
|
17
|
+
* @param error - The error to parse
|
|
18
|
+
* @returns The parsed ArkError, or undefined if the error is not an ArkError
|
|
19
|
+
*/
|
|
20
|
+
function maybeArkError(error) {
|
|
21
|
+
try {
|
|
22
|
+
if (!(error instanceof Error))
|
|
23
|
+
return undefined;
|
|
24
|
+
const decoded = JSON.parse(error.message);
|
|
25
|
+
if (!("details" in decoded))
|
|
26
|
+
return undefined;
|
|
27
|
+
if (!Array.isArray(decoded.details))
|
|
28
|
+
return undefined;
|
|
29
|
+
// search for a valid details object with the correct type
|
|
30
|
+
for (const details of decoded.details) {
|
|
31
|
+
if (!("@type" in details))
|
|
32
|
+
continue;
|
|
33
|
+
const type = details["@type"];
|
|
34
|
+
if (type !== "type.googleapis.com/ark.v1.ErrorDetails")
|
|
35
|
+
continue;
|
|
36
|
+
if (!("code" in details))
|
|
37
|
+
continue;
|
|
38
|
+
const code = details.code;
|
|
39
|
+
if (!("message" in details))
|
|
40
|
+
continue;
|
|
41
|
+
const message = details.message;
|
|
42
|
+
if (!("name" in details))
|
|
43
|
+
continue;
|
|
44
|
+
const name = details.name;
|
|
45
|
+
let metadata;
|
|
46
|
+
if ("metadata" in details && isMetadata(details.metadata)) {
|
|
47
|
+
metadata = details.metadata;
|
|
48
|
+
}
|
|
49
|
+
return new ArkError(code, message, name, metadata);
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isMetadata(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
59
|
+
}
|
|
@@ -61,18 +61,6 @@ class WalletRepositoryImpl {
|
|
|
61
61
|
return [];
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
async saveVtxo(address, vtxo) {
|
|
65
|
-
const vtxos = await this.getVtxos(address);
|
|
66
|
-
const existing = vtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout);
|
|
67
|
-
if (existing !== -1) {
|
|
68
|
-
vtxos[existing] = vtxo;
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
vtxos.push(vtxo);
|
|
72
|
-
}
|
|
73
|
-
this.cache.vtxos.set(address, vtxos.slice());
|
|
74
|
-
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
|
|
75
|
-
}
|
|
76
64
|
async saveVtxos(address, vtxos) {
|
|
77
65
|
const storedVtxos = await this.getVtxos(address);
|
|
78
66
|
for (const vtxo of vtxos) {
|
|
@@ -119,20 +107,6 @@ class WalletRepositoryImpl {
|
|
|
119
107
|
return [];
|
|
120
108
|
}
|
|
121
109
|
}
|
|
122
|
-
async saveTransaction(address, tx) {
|
|
123
|
-
const transactions = await this.getTransactionHistory(address);
|
|
124
|
-
const existing = transactions.findIndex((t) => t.key === tx.key);
|
|
125
|
-
if (existing !== -1) {
|
|
126
|
-
transactions[existing] = tx;
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
transactions.push(tx);
|
|
130
|
-
}
|
|
131
|
-
// Sort by createdAt descending
|
|
132
|
-
transactions.sort((a, b) => b.createdAt - a.createdAt);
|
|
133
|
-
this.cache.transactions.set(address, transactions);
|
|
134
|
-
await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
|
|
135
|
-
}
|
|
136
110
|
async saveTransactions(address, txs) {
|
|
137
111
|
const storedTransactions = await this.getTransactionHistory(address);
|
|
138
112
|
for (const tx of txs) {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Response = void 0;
|
|
4
|
+
const base_1 = require("@scure/base");
|
|
5
|
+
function getRandomId() {
|
|
6
|
+
const randomValue = crypto.getRandomValues(new Uint8Array(16));
|
|
7
|
+
return base_1.hex.encode(randomValue);
|
|
8
|
+
}
|
|
4
9
|
/**
|
|
5
10
|
* Response is the namespace that contains the response types for the service worker.
|
|
6
11
|
*/
|
|
@@ -187,4 +192,31 @@ var Response;
|
|
|
187
192
|
};
|
|
188
193
|
}
|
|
189
194
|
Response.walletReloaded = walletReloaded;
|
|
195
|
+
function isVtxoUpdate(response) {
|
|
196
|
+
return response.type === "VTXO_UPDATE";
|
|
197
|
+
}
|
|
198
|
+
Response.isVtxoUpdate = isVtxoUpdate;
|
|
199
|
+
function vtxoUpdate(newVtxos, spentVtxos) {
|
|
200
|
+
return {
|
|
201
|
+
type: "VTXO_UPDATE",
|
|
202
|
+
id: getRandomId(), // spontaneous update, not tied to a request
|
|
203
|
+
success: true,
|
|
204
|
+
spentVtxos,
|
|
205
|
+
newVtxos,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
Response.vtxoUpdate = vtxoUpdate;
|
|
209
|
+
function isUtxoUpdate(response) {
|
|
210
|
+
return response.type === "UTXO_UPDATE";
|
|
211
|
+
}
|
|
212
|
+
Response.isUtxoUpdate = isUtxoUpdate;
|
|
213
|
+
function utxoUpdate(coins) {
|
|
214
|
+
return {
|
|
215
|
+
type: "UTXO_UPDATE",
|
|
216
|
+
id: getRandomId(), // spontaneous update, not tied to a request
|
|
217
|
+
success: true,
|
|
218
|
+
coins,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
Response.utxoUpdate = utxoUpdate;
|
|
190
222
|
})(Response || (exports.Response = Response = {}));
|
|
@@ -256,6 +256,9 @@ class ServiceWorkerWallet {
|
|
|
256
256
|
return new Promise((resolve, reject) => {
|
|
257
257
|
const messageHandler = (event) => {
|
|
258
258
|
const response = event.data;
|
|
259
|
+
if (response.id !== message.id) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
259
262
|
if (!response.success) {
|
|
260
263
|
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
261
264
|
reject(new Error(response.message));
|
|
@@ -130,11 +130,11 @@ class Worker {
|
|
|
130
130
|
...spentVtxos,
|
|
131
131
|
]);
|
|
132
132
|
// notify all clients about the vtxo update
|
|
133
|
-
this.sendMessageToAllClients(
|
|
133
|
+
this.sendMessageToAllClients(response_1.Response.vtxoUpdate(newVtxos, spentVtxos));
|
|
134
134
|
}
|
|
135
135
|
if (funds.type === "utxo") {
|
|
136
136
|
// notify all clients about the utxo update
|
|
137
|
-
this.sendMessageToAllClients(
|
|
137
|
+
this.sendMessageToAllClients(response_1.Response.utxoUpdate(funds.coins));
|
|
138
138
|
}
|
|
139
139
|
});
|
|
140
140
|
}
|
|
@@ -518,15 +518,12 @@ class Worker {
|
|
|
518
518
|
event.source?.postMessage(response_1.Response.error(message.id, "Unknown message type"));
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
|
-
async sendMessageToAllClients(
|
|
521
|
+
async sendMessageToAllClients(message) {
|
|
522
522
|
self.clients
|
|
523
523
|
.matchAll({ includeUncontrolled: true, type: "window" })
|
|
524
524
|
.then((clients) => {
|
|
525
525
|
clients.forEach((client) => {
|
|
526
|
-
client.postMessage(
|
|
527
|
-
type,
|
|
528
|
-
message,
|
|
529
|
-
});
|
|
526
|
+
client.postMessage(message);
|
|
530
527
|
});
|
|
531
528
|
});
|
|
532
529
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -28,6 +28,7 @@ import { P2A } from './utils/anchor.js';
|
|
|
28
28
|
import { Unroll } from './wallet/unroll.js';
|
|
29
29
|
import { WalletRepositoryImpl } from './repositories/walletRepository.js';
|
|
30
30
|
import { ContractRepositoryImpl } from './repositories/contractRepository.js';
|
|
31
|
+
import { ArkError, maybeArkError } from './providers/errors.js';
|
|
31
32
|
export {
|
|
32
33
|
// Wallets
|
|
33
34
|
Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager,
|
|
@@ -56,4 +57,6 @@ Intent,
|
|
|
56
57
|
// TxTree
|
|
57
58
|
TxTree,
|
|
58
59
|
// Anchor
|
|
59
|
-
P2A, Unroll, Transaction,
|
|
60
|
+
P2A, Unroll, Transaction,
|
|
61
|
+
// Errors
|
|
62
|
+
ArkError, maybeArkError, };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hex } from "@scure/base";
|
|
2
2
|
import { eventSourceIterator } from './utils.js';
|
|
3
|
+
import { maybeArkError } from './errors.js';
|
|
3
4
|
export var SettlementEventType;
|
|
4
5
|
(function (SettlementEventType) {
|
|
5
6
|
SettlementEventType["BatchStarted"] = "batch_started";
|
|
@@ -28,7 +29,8 @@ export class RestArkProvider {
|
|
|
28
29
|
const url = `${this.serverUrl}/v1/info`;
|
|
29
30
|
const response = await fetch(url);
|
|
30
31
|
if (!response.ok) {
|
|
31
|
-
|
|
32
|
+
const errorText = await response.text();
|
|
33
|
+
handleError(errorText, `Failed to get server info: ${response.statusText}`);
|
|
32
34
|
}
|
|
33
35
|
const fromServer = await response.json();
|
|
34
36
|
return {
|
|
@@ -78,16 +80,7 @@ export class RestArkProvider {
|
|
|
78
80
|
});
|
|
79
81
|
if (!response.ok) {
|
|
80
82
|
const errorText = await response.text();
|
|
81
|
-
|
|
82
|
-
const grpcError = JSON.parse(errorText);
|
|
83
|
-
// gRPC errors usually have a message and code field
|
|
84
|
-
throw new Error(`Failed to submit virtual transaction: ${grpcError.message || grpcError.error || errorText}`);
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
86
|
-
}
|
|
87
|
-
catch (_) {
|
|
88
|
-
// If JSON parse fails, use the raw error text
|
|
89
|
-
throw new Error(`Failed to submit virtual transaction: ${errorText}`);
|
|
90
|
-
}
|
|
83
|
+
handleError(errorText, `Failed to submit virtual transaction: ${errorText}`);
|
|
91
84
|
}
|
|
92
85
|
const data = await response.json();
|
|
93
86
|
return {
|
|
@@ -110,7 +103,7 @@ export class RestArkProvider {
|
|
|
110
103
|
});
|
|
111
104
|
if (!response.ok) {
|
|
112
105
|
const errorText = await response.text();
|
|
113
|
-
|
|
106
|
+
handleError(errorText, `Failed to finalize offchain transaction: ${errorText}`);
|
|
114
107
|
}
|
|
115
108
|
}
|
|
116
109
|
async registerIntent(intent) {
|
|
@@ -129,7 +122,7 @@ export class RestArkProvider {
|
|
|
129
122
|
});
|
|
130
123
|
if (!response.ok) {
|
|
131
124
|
const errorText = await response.text();
|
|
132
|
-
|
|
125
|
+
handleError(errorText, `Failed to register intent: ${errorText}`);
|
|
133
126
|
}
|
|
134
127
|
const data = await response.json();
|
|
135
128
|
return data.intentId;
|
|
@@ -150,7 +143,7 @@ export class RestArkProvider {
|
|
|
150
143
|
});
|
|
151
144
|
if (!response.ok) {
|
|
152
145
|
const errorText = await response.text();
|
|
153
|
-
|
|
146
|
+
handleError(errorText, `Failed to delete intent: ${errorText}`);
|
|
154
147
|
}
|
|
155
148
|
}
|
|
156
149
|
async confirmRegistration(intentId) {
|
|
@@ -166,7 +159,7 @@ export class RestArkProvider {
|
|
|
166
159
|
});
|
|
167
160
|
if (!response.ok) {
|
|
168
161
|
const errorText = await response.text();
|
|
169
|
-
|
|
162
|
+
handleError(errorText, `Failed to confirm registration: ${errorText}`);
|
|
170
163
|
}
|
|
171
164
|
}
|
|
172
165
|
async submitTreeNonces(batchId, pubkey, nonces) {
|
|
@@ -184,7 +177,7 @@ export class RestArkProvider {
|
|
|
184
177
|
});
|
|
185
178
|
if (!response.ok) {
|
|
186
179
|
const errorText = await response.text();
|
|
187
|
-
|
|
180
|
+
handleError(errorText, `Failed to submit tree nonces: ${errorText}`);
|
|
188
181
|
}
|
|
189
182
|
}
|
|
190
183
|
async submitTreeSignatures(batchId, pubkey, signatures) {
|
|
@@ -202,7 +195,7 @@ export class RestArkProvider {
|
|
|
202
195
|
});
|
|
203
196
|
if (!response.ok) {
|
|
204
197
|
const errorText = await response.text();
|
|
205
|
-
|
|
198
|
+
handleError(errorText, `Failed to submit tree signatures: ${errorText}`);
|
|
206
199
|
}
|
|
207
200
|
}
|
|
208
201
|
async submitSignedForfeitTxs(signedForfeitTxs, signedCommitmentTx) {
|
|
@@ -218,7 +211,8 @@ export class RestArkProvider {
|
|
|
218
211
|
}),
|
|
219
212
|
});
|
|
220
213
|
if (!response.ok) {
|
|
221
|
-
|
|
214
|
+
const errorText = await response.text();
|
|
215
|
+
handleError(errorText, `Failed to submit forfeit transactions: ${response.statusText}`);
|
|
222
216
|
}
|
|
223
217
|
}
|
|
224
218
|
async *getEventStream(signal, topics) {
|
|
@@ -327,16 +321,7 @@ export class RestArkProvider {
|
|
|
327
321
|
});
|
|
328
322
|
if (!response.ok) {
|
|
329
323
|
const errorText = await response.text();
|
|
330
|
-
|
|
331
|
-
const grpcError = JSON.parse(errorText);
|
|
332
|
-
// gRPC errors usually have a message and code field
|
|
333
|
-
throw new Error(`Failed to get pending transactions: ${grpcError.message || grpcError.error || errorText}`);
|
|
334
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
335
|
-
}
|
|
336
|
-
catch (_) {
|
|
337
|
-
// If JSON parse fails, use the raw error text
|
|
338
|
-
throw new Error(`Failed to get pending transactions: ${errorText}`);
|
|
339
|
-
}
|
|
324
|
+
handleError(errorText, `Failed to get pending transactions: ${errorText}`);
|
|
340
325
|
}
|
|
341
326
|
const data = await response.json();
|
|
342
327
|
return data.pendingTxs;
|
|
@@ -519,3 +504,8 @@ function mapVtxo(vtxo) {
|
|
|
519
504
|
arkTxid: vtxo.arkTxid,
|
|
520
505
|
};
|
|
521
506
|
}
|
|
507
|
+
function handleError(errorText, defaultMessage) {
|
|
508
|
+
const error = new Error(errorText);
|
|
509
|
+
const arkError = maybeArkError(error);
|
|
510
|
+
throw arkError ?? new Error(defaultMessage);
|
|
511
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export class ArkError extends Error {
|
|
2
|
+
constructor(code, message, name, metadata) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code;
|
|
5
|
+
this.message = message;
|
|
6
|
+
this.name = name;
|
|
7
|
+
this.metadata = metadata;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Try to convert an error to an ArkError class, returning undefined if the error is not an ArkError
|
|
12
|
+
* @param error - The error to parse
|
|
13
|
+
* @returns The parsed ArkError, or undefined if the error is not an ArkError
|
|
14
|
+
*/
|
|
15
|
+
export function maybeArkError(error) {
|
|
16
|
+
try {
|
|
17
|
+
if (!(error instanceof Error))
|
|
18
|
+
return undefined;
|
|
19
|
+
const decoded = JSON.parse(error.message);
|
|
20
|
+
if (!("details" in decoded))
|
|
21
|
+
return undefined;
|
|
22
|
+
if (!Array.isArray(decoded.details))
|
|
23
|
+
return undefined;
|
|
24
|
+
// search for a valid details object with the correct type
|
|
25
|
+
for (const details of decoded.details) {
|
|
26
|
+
if (!("@type" in details))
|
|
27
|
+
continue;
|
|
28
|
+
const type = details["@type"];
|
|
29
|
+
if (type !== "type.googleapis.com/ark.v1.ErrorDetails")
|
|
30
|
+
continue;
|
|
31
|
+
if (!("code" in details))
|
|
32
|
+
continue;
|
|
33
|
+
const code = details.code;
|
|
34
|
+
if (!("message" in details))
|
|
35
|
+
continue;
|
|
36
|
+
const message = details.message;
|
|
37
|
+
if (!("name" in details))
|
|
38
|
+
continue;
|
|
39
|
+
const name = details.name;
|
|
40
|
+
let metadata;
|
|
41
|
+
if ("metadata" in details && isMetadata(details.metadata)) {
|
|
42
|
+
metadata = details.metadata;
|
|
43
|
+
}
|
|
44
|
+
return new ArkError(code, message, name, metadata);
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isMetadata(value) {
|
|
53
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
54
|
+
}
|
|
@@ -58,18 +58,6 @@ export class WalletRepositoryImpl {
|
|
|
58
58
|
return [];
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
async saveVtxo(address, vtxo) {
|
|
62
|
-
const vtxos = await this.getVtxos(address);
|
|
63
|
-
const existing = vtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout);
|
|
64
|
-
if (existing !== -1) {
|
|
65
|
-
vtxos[existing] = vtxo;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
vtxos.push(vtxo);
|
|
69
|
-
}
|
|
70
|
-
this.cache.vtxos.set(address, vtxos.slice());
|
|
71
|
-
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
|
|
72
|
-
}
|
|
73
61
|
async saveVtxos(address, vtxos) {
|
|
74
62
|
const storedVtxos = await this.getVtxos(address);
|
|
75
63
|
for (const vtxo of vtxos) {
|
|
@@ -116,20 +104,6 @@ export class WalletRepositoryImpl {
|
|
|
116
104
|
return [];
|
|
117
105
|
}
|
|
118
106
|
}
|
|
119
|
-
async saveTransaction(address, tx) {
|
|
120
|
-
const transactions = await this.getTransactionHistory(address);
|
|
121
|
-
const existing = transactions.findIndex((t) => t.key === tx.key);
|
|
122
|
-
if (existing !== -1) {
|
|
123
|
-
transactions[existing] = tx;
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
transactions.push(tx);
|
|
127
|
-
}
|
|
128
|
-
// Sort by createdAt descending
|
|
129
|
-
transactions.sort((a, b) => b.createdAt - a.createdAt);
|
|
130
|
-
this.cache.transactions.set(address, transactions);
|
|
131
|
-
await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
|
|
132
|
-
}
|
|
133
107
|
async saveTransactions(address, txs) {
|
|
134
108
|
const storedTransactions = await this.getTransactionHistory(address);
|
|
135
109
|
for (const tx of txs) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { hex } from "@scure/base";
|
|
2
|
+
function getRandomId() {
|
|
3
|
+
const randomValue = crypto.getRandomValues(new Uint8Array(16));
|
|
4
|
+
return hex.encode(randomValue);
|
|
5
|
+
}
|
|
1
6
|
/**
|
|
2
7
|
* Response is the namespace that contains the response types for the service worker.
|
|
3
8
|
*/
|
|
@@ -184,4 +189,31 @@ export var Response;
|
|
|
184
189
|
};
|
|
185
190
|
}
|
|
186
191
|
Response.walletReloaded = walletReloaded;
|
|
192
|
+
function isVtxoUpdate(response) {
|
|
193
|
+
return response.type === "VTXO_UPDATE";
|
|
194
|
+
}
|
|
195
|
+
Response.isVtxoUpdate = isVtxoUpdate;
|
|
196
|
+
function vtxoUpdate(newVtxos, spentVtxos) {
|
|
197
|
+
return {
|
|
198
|
+
type: "VTXO_UPDATE",
|
|
199
|
+
id: getRandomId(), // spontaneous update, not tied to a request
|
|
200
|
+
success: true,
|
|
201
|
+
spentVtxos,
|
|
202
|
+
newVtxos,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
Response.vtxoUpdate = vtxoUpdate;
|
|
206
|
+
function isUtxoUpdate(response) {
|
|
207
|
+
return response.type === "UTXO_UPDATE";
|
|
208
|
+
}
|
|
209
|
+
Response.isUtxoUpdate = isUtxoUpdate;
|
|
210
|
+
function utxoUpdate(coins) {
|
|
211
|
+
return {
|
|
212
|
+
type: "UTXO_UPDATE",
|
|
213
|
+
id: getRandomId(), // spontaneous update, not tied to a request
|
|
214
|
+
success: true,
|
|
215
|
+
coins,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
Response.utxoUpdate = utxoUpdate;
|
|
187
219
|
})(Response || (Response = {}));
|
|
@@ -253,6 +253,9 @@ export class ServiceWorkerWallet {
|
|
|
253
253
|
return new Promise((resolve, reject) => {
|
|
254
254
|
const messageHandler = (event) => {
|
|
255
255
|
const response = event.data;
|
|
256
|
+
if (response.id !== message.id) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
256
259
|
if (!response.success) {
|
|
257
260
|
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
258
261
|
reject(new Error(response.message));
|
|
@@ -127,11 +127,11 @@ export class Worker {
|
|
|
127
127
|
...spentVtxos,
|
|
128
128
|
]);
|
|
129
129
|
// notify all clients about the vtxo update
|
|
130
|
-
this.sendMessageToAllClients(
|
|
130
|
+
this.sendMessageToAllClients(Response.vtxoUpdate(newVtxos, spentVtxos));
|
|
131
131
|
}
|
|
132
132
|
if (funds.type === "utxo") {
|
|
133
133
|
// notify all clients about the utxo update
|
|
134
|
-
this.sendMessageToAllClients(
|
|
134
|
+
this.sendMessageToAllClients(Response.utxoUpdate(funds.coins));
|
|
135
135
|
}
|
|
136
136
|
});
|
|
137
137
|
}
|
|
@@ -515,15 +515,12 @@ export class Worker {
|
|
|
515
515
|
event.source?.postMessage(Response.error(message.id, "Unknown message type"));
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
|
-
async sendMessageToAllClients(
|
|
518
|
+
async sendMessageToAllClients(message) {
|
|
519
519
|
self.clients
|
|
520
520
|
.matchAll({ includeUncontrolled: true, type: "window" })
|
|
521
521
|
.then((clients) => {
|
|
522
522
|
clients.forEach((client) => {
|
|
523
|
-
client.postMessage(
|
|
524
|
-
type,
|
|
525
|
-
message,
|
|
526
|
-
});
|
|
523
|
+
client.postMessage(message);
|
|
527
524
|
});
|
|
528
525
|
});
|
|
529
526
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -32,5 +32,6 @@ import { AnchorBumper, P2A } from "./utils/anchor";
|
|
|
32
32
|
import { Unroll } from "./wallet/unroll";
|
|
33
33
|
import { WalletRepositoryImpl } from "./repositories/walletRepository";
|
|
34
34
|
import { ContractRepositoryImpl } from "./repositories/contractRepository";
|
|
35
|
-
|
|
35
|
+
import { ArkError, maybeArkError } from "./providers/errors";
|
|
36
|
+
export { Wallet, SingleKey, OnchainWallet, Ramps, VtxoManager, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, Worker, ServiceWorkerWallet, Request, Response, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, ArkNote, networks, WalletRepositoryImpl, ContractRepositoryImpl, Intent, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, };
|
|
36
37
|
export type { Identity, IWallet, WalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, Recipient, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, IndexerProvider, PageResponse, Batch, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, ArkInfo, SignedIntent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class ArkError extends Error {
|
|
2
|
+
readonly code: number;
|
|
3
|
+
readonly message: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly metadata?: Record<string, string> | undefined;
|
|
6
|
+
constructor(code: number, message: string, name: string, metadata?: Record<string, string> | undefined);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Try to convert an error to an ArkError class, returning undefined if the error is not an ArkError
|
|
10
|
+
* @param error - The error to parse
|
|
11
|
+
* @returns The parsed ArkError, or undefined if the error is not an ArkError
|
|
12
|
+
*/
|
|
13
|
+
export declare function maybeArkError(error: any): ArkError | undefined;
|
|
@@ -6,12 +6,10 @@ export interface WalletState {
|
|
|
6
6
|
}
|
|
7
7
|
export interface WalletRepository {
|
|
8
8
|
getVtxos(address: string): Promise<ExtendedVirtualCoin[]>;
|
|
9
|
-
saveVtxo(address: string, vtxo: ExtendedVirtualCoin): Promise<void>;
|
|
10
9
|
saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
|
|
11
10
|
removeVtxo(address: string, vtxoId: string): Promise<void>;
|
|
12
11
|
clearVtxos(address: string): Promise<void>;
|
|
13
12
|
getTransactionHistory(address: string): Promise<ArkTransaction[]>;
|
|
14
|
-
saveTransaction(address: string, tx: ArkTransaction): Promise<void>;
|
|
15
13
|
saveTransactions(address: string, txs: ArkTransaction[]): Promise<void>;
|
|
16
14
|
clearTransactions(address: string): Promise<void>;
|
|
17
15
|
getWalletState(): Promise<WalletState | null>;
|
|
@@ -22,12 +20,10 @@ export declare class WalletRepositoryImpl implements WalletRepository {
|
|
|
22
20
|
private cache;
|
|
23
21
|
constructor(storage: StorageAdapter);
|
|
24
22
|
getVtxos(address: string): Promise<ExtendedVirtualCoin[]>;
|
|
25
|
-
saveVtxo(address: string, vtxo: ExtendedVirtualCoin): Promise<void>;
|
|
26
23
|
saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise<void>;
|
|
27
24
|
removeVtxo(address: string, vtxoId: string): Promise<void>;
|
|
28
25
|
clearVtxos(address: string): Promise<void>;
|
|
29
26
|
getTransactionHistory(address: string): Promise<ArkTransaction[]>;
|
|
30
|
-
saveTransaction(address: string, tx: ArkTransaction): Promise<void>;
|
|
31
27
|
saveTransactions(address: string, txs: ArkTransaction[]): Promise<void>;
|
|
32
28
|
clearTransactions(address: string): Promise<void>;
|
|
33
29
|
getWalletState(): Promise<WalletState | null>;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { WalletBalance, VirtualCoin, ArkTransaction, IWallet } from "..";
|
|
1
|
+
import { WalletBalance, VirtualCoin, ArkTransaction, IWallet, Coin } from "..";
|
|
2
|
+
import { ExtendedVirtualCoin } from "../..";
|
|
2
3
|
import { SettlementEvent } from "../../providers/ark";
|
|
3
4
|
/**
|
|
4
5
|
* Response is the namespace that contains the response types for the service worker.
|
|
5
6
|
*/
|
|
6
7
|
export declare namespace Response {
|
|
7
|
-
type Type = "WALLET_INITIALIZED" | "WALLET_RELOADED" | "SETTLE_EVENT" | "SETTLE_SUCCESS" | "ADDRESS" | "BOARDING_ADDRESS" | "BALANCE" | "VTXOS" | "VIRTUAL_COINS" | "BOARDING_UTXOS" | "SEND_BITCOIN_SUCCESS" | "TRANSACTION_HISTORY" | "WALLET_STATUS" | "ERROR" | "CLEAR_RESPONSE";
|
|
8
|
+
type Type = "WALLET_INITIALIZED" | "WALLET_RELOADED" | "SETTLE_EVENT" | "SETTLE_SUCCESS" | "ADDRESS" | "BOARDING_ADDRESS" | "BALANCE" | "VTXOS" | "VIRTUAL_COINS" | "BOARDING_UTXOS" | "SEND_BITCOIN_SUCCESS" | "TRANSACTION_HISTORY" | "WALLET_STATUS" | "ERROR" | "CLEAR_RESPONSE" | "VTXO_UPDATE" | "UTXO_UPDATE";
|
|
8
9
|
interface Base {
|
|
9
10
|
type: Type;
|
|
10
11
|
success: boolean;
|
|
@@ -106,4 +107,17 @@ export declare namespace Response {
|
|
|
106
107
|
}
|
|
107
108
|
function isWalletReloaded(response: Base): response is WalletReloaded;
|
|
108
109
|
function walletReloaded(id: string, success: boolean): WalletReloaded;
|
|
110
|
+
interface VtxoUpdate extends Base {
|
|
111
|
+
type: "VTXO_UPDATE";
|
|
112
|
+
spentVtxos: ExtendedVirtualCoin[];
|
|
113
|
+
newVtxos: ExtendedVirtualCoin[];
|
|
114
|
+
}
|
|
115
|
+
function isVtxoUpdate(response: Base): response is VtxoUpdate;
|
|
116
|
+
function vtxoUpdate(newVtxos: ExtendedVirtualCoin[], spentVtxos: ExtendedVirtualCoin[]): VtxoUpdate;
|
|
117
|
+
interface UtxoUpdate extends Base {
|
|
118
|
+
type: "UTXO_UPDATE";
|
|
119
|
+
coins: Coin[];
|
|
120
|
+
}
|
|
121
|
+
function isUtxoUpdate(response: Base): response is UtxoUpdate;
|
|
122
|
+
function utxoUpdate(coins: Coin[]): UtxoUpdate;
|
|
109
123
|
}
|