@arkade-os/sdk 0.1.4 → 0.2.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 +157 -174
- package/dist/cjs/arknote/index.js +61 -58
- package/dist/cjs/bip322/errors.js +13 -0
- package/dist/cjs/bip322/index.js +178 -0
- package/dist/cjs/forfeit.js +14 -25
- package/dist/cjs/identity/singleKey.js +68 -0
- package/dist/cjs/index.js +43 -17
- package/dist/cjs/providers/ark.js +261 -321
- package/dist/cjs/providers/indexer.js +525 -0
- package/dist/cjs/providers/onchain.js +193 -15
- package/dist/cjs/script/address.js +48 -17
- package/dist/cjs/script/base.js +120 -3
- package/dist/cjs/script/default.js +18 -4
- package/dist/cjs/script/tapscript.js +61 -20
- package/dist/cjs/script/vhtlc.js +85 -7
- package/dist/cjs/tree/signingSession.js +63 -106
- package/dist/cjs/tree/txTree.js +193 -0
- package/dist/cjs/tree/validation.js +79 -155
- package/dist/cjs/utils/anchor.js +35 -0
- package/dist/cjs/utils/arkTransaction.js +108 -0
- package/dist/cjs/utils/transactionHistory.js +84 -72
- package/dist/cjs/utils/txSizeEstimator.js +12 -0
- package/dist/cjs/utils/unknownFields.js +211 -0
- package/dist/cjs/wallet/index.js +12 -0
- package/dist/cjs/wallet/onchain.js +201 -0
- package/dist/cjs/wallet/ramps.js +95 -0
- package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +32 -0
- package/dist/cjs/wallet/serviceWorker/request.js +15 -12
- package/dist/cjs/wallet/serviceWorker/response.js +22 -27
- package/dist/cjs/wallet/serviceWorker/utils.js +8 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +61 -34
- package/dist/cjs/wallet/serviceWorker/worker.js +120 -108
- package/dist/cjs/wallet/unroll.js +270 -0
- package/dist/cjs/wallet/wallet.js +701 -454
- package/dist/esm/arknote/index.js +61 -57
- package/dist/esm/bip322/errors.js +9 -0
- package/dist/esm/bip322/index.js +174 -0
- package/dist/esm/forfeit.js +15 -26
- package/dist/esm/identity/singleKey.js +64 -0
- package/dist/esm/index.js +31 -12
- package/dist/esm/providers/ark.js +259 -320
- package/dist/esm/providers/indexer.js +521 -0
- package/dist/esm/providers/onchain.js +193 -15
- package/dist/esm/script/address.js +48 -17
- package/dist/esm/script/base.js +120 -3
- package/dist/esm/script/default.js +18 -4
- package/dist/esm/script/tapscript.js +61 -20
- package/dist/esm/script/vhtlc.js +85 -7
- package/dist/esm/tree/signingSession.js +65 -108
- package/dist/esm/tree/txTree.js +189 -0
- package/dist/esm/tree/validation.js +75 -152
- package/dist/esm/utils/anchor.js +31 -0
- package/dist/esm/utils/arkTransaction.js +105 -0
- package/dist/esm/utils/transactionHistory.js +84 -72
- package/dist/esm/utils/txSizeEstimator.js +12 -0
- package/dist/esm/utils/unknownFields.js +173 -0
- package/dist/esm/wallet/index.js +9 -0
- package/dist/esm/wallet/onchain.js +196 -0
- package/dist/esm/wallet/ramps.js +91 -0
- package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +32 -0
- package/dist/esm/wallet/serviceWorker/request.js +15 -12
- package/dist/esm/wallet/serviceWorker/response.js +22 -27
- package/dist/esm/wallet/serviceWorker/utils.js +8 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +62 -35
- package/dist/esm/wallet/serviceWorker/worker.js +120 -108
- package/dist/esm/wallet/unroll.js +267 -0
- package/dist/esm/wallet/wallet.js +674 -461
- package/dist/types/arknote/index.d.ts +40 -13
- package/dist/types/bip322/errors.d.ts +6 -0
- package/dist/types/bip322/index.d.ts +57 -0
- package/dist/types/forfeit.d.ts +2 -14
- package/dist/types/identity/singleKey.d.ts +27 -0
- package/dist/types/index.d.ts +24 -12
- package/dist/types/providers/ark.d.ts +114 -95
- package/dist/types/providers/indexer.d.ts +186 -0
- package/dist/types/providers/onchain.d.ts +41 -11
- package/dist/types/script/address.d.ts +26 -2
- package/dist/types/script/base.d.ts +13 -3
- package/dist/types/script/default.d.ts +22 -0
- package/dist/types/script/tapscript.d.ts +61 -5
- package/dist/types/script/vhtlc.d.ts +27 -0
- package/dist/types/tree/signingSession.d.ts +5 -5
- package/dist/types/tree/txTree.d.ts +28 -0
- package/dist/types/tree/validation.d.ts +15 -22
- package/dist/types/utils/anchor.d.ts +19 -0
- package/dist/types/utils/arkTransaction.d.ts +27 -0
- package/dist/types/utils/transactionHistory.d.ts +7 -1
- package/dist/types/utils/txSizeEstimator.d.ts +3 -0
- package/dist/types/utils/unknownFields.d.ts +83 -0
- package/dist/types/wallet/index.d.ts +51 -50
- package/dist/types/wallet/onchain.d.ts +49 -0
- package/dist/types/wallet/ramps.d.ts +32 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/request.d.ts +14 -16
- package/dist/types/wallet/serviceWorker/response.d.ts +17 -19
- package/dist/types/wallet/serviceWorker/utils.d.ts +8 -0
- package/dist/types/wallet/serviceWorker/wallet.d.ts +36 -8
- package/dist/types/wallet/serviceWorker/worker.d.ts +7 -3
- package/dist/types/wallet/unroll.d.ts +102 -0
- package/dist/types/wallet/wallet.d.ts +71 -25
- package/package.json +37 -35
- package/dist/cjs/identity/inMemoryKey.js +0 -40
- package/dist/cjs/tree/vtxoTree.js +0 -231
- package/dist/cjs/utils/coinselect.js +0 -73
- package/dist/cjs/utils/psbt.js +0 -137
- package/dist/esm/identity/inMemoryKey.js +0 -36
- package/dist/esm/tree/vtxoTree.js +0 -191
- package/dist/esm/utils/coinselect.js +0 -69
- package/dist/esm/utils/psbt.js +0 -131
- package/dist/types/identity/inMemoryKey.d.ts +0 -12
- package/dist/types/tree/vtxoTree.d.ts +0 -33
- package/dist/types/utils/coinselect.d.ts +0 -21
- package/dist/types/utils/psbt.d.ts +0 -11
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RestArkProvider = exports.SettlementEventType = void 0;
|
|
4
|
-
|
|
4
|
+
exports.isFetchTimeoutError = isFetchTimeoutError;
|
|
5
5
|
const base_1 = require("@scure/base");
|
|
6
6
|
var SettlementEventType;
|
|
7
7
|
(function (SettlementEventType) {
|
|
8
|
-
SettlementEventType["
|
|
9
|
-
SettlementEventType["
|
|
10
|
-
SettlementEventType["
|
|
11
|
-
SettlementEventType["
|
|
12
|
-
SettlementEventType["
|
|
8
|
+
SettlementEventType["BatchStarted"] = "batch_started";
|
|
9
|
+
SettlementEventType["BatchFinalization"] = "batch_finalization";
|
|
10
|
+
SettlementEventType["BatchFinalized"] = "batch_finalized";
|
|
11
|
+
SettlementEventType["BatchFailed"] = "batch_failed";
|
|
12
|
+
SettlementEventType["TreeSigningStarted"] = "tree_signing_started";
|
|
13
|
+
SettlementEventType["TreeNoncesAggregated"] = "tree_nonces_aggregated";
|
|
14
|
+
SettlementEventType["TreeTx"] = "tree_tx";
|
|
15
|
+
SettlementEventType["TreeSignature"] = "tree_signature";
|
|
13
16
|
})(SettlementEventType || (exports.SettlementEventType = SettlementEventType = {}));
|
|
17
|
+
/**
|
|
18
|
+
* REST-based Ark provider implementation.
|
|
19
|
+
* @see https://buf.build/arkade-os/arkd/docs/main:ark.v1#ark.v1.ArkService
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const provider = new RestArkProvider('https://ark.example.com');
|
|
23
|
+
* const info = await provider.getInfo();
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
14
26
|
class RestArkProvider {
|
|
15
27
|
constructor(serverUrl) {
|
|
16
28
|
this.serverUrl = serverUrl;
|
|
@@ -24,48 +36,35 @@ class RestArkProvider {
|
|
|
24
36
|
const fromServer = await response.json();
|
|
25
37
|
return {
|
|
26
38
|
...fromServer,
|
|
39
|
+
vtxoTreeExpiry: BigInt(fromServer.vtxoTreeExpiry ?? 0),
|
|
27
40
|
unilateralExitDelay: BigInt(fromServer.unilateralExitDelay ?? 0),
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
async getRound(txid) {
|
|
44
|
-
const url = `${this.serverUrl}/v1/round/${txid}`;
|
|
45
|
-
const response = await fetch(url);
|
|
46
|
-
if (!response.ok) {
|
|
47
|
-
throw new Error(`Failed to fetch round: ${response.statusText}`);
|
|
48
|
-
}
|
|
49
|
-
const data = (await response.json());
|
|
50
|
-
const round = data.round;
|
|
51
|
-
return {
|
|
52
|
-
id: round.id,
|
|
53
|
-
start: new Date(Number(round.start) * 1000), // Convert from Unix timestamp to Date
|
|
54
|
-
end: new Date(Number(round.end) * 1000), // Convert from Unix timestamp to Date
|
|
55
|
-
vtxoTree: this.toTxTree(round.vtxoTree),
|
|
56
|
-
forfeitTxs: round.forfeitTxs || [],
|
|
57
|
-
connectors: this.toTxTree(round.connectors),
|
|
41
|
+
roundInterval: BigInt(fromServer.roundInterval ?? 0),
|
|
42
|
+
dust: BigInt(fromServer.dust ?? 0),
|
|
43
|
+
utxoMinAmount: BigInt(fromServer.utxoMinAmount ?? 0),
|
|
44
|
+
utxoMaxAmount: BigInt(fromServer.utxoMaxAmount ?? -1),
|
|
45
|
+
vtxoMinAmount: BigInt(fromServer.vtxoMinAmount ?? 0),
|
|
46
|
+
vtxoMaxAmount: BigInt(fromServer.vtxoMaxAmount ?? -1),
|
|
47
|
+
boardingExitDelay: BigInt(fromServer.boardingExitDelay ?? 0),
|
|
48
|
+
marketHour: "marketHour" in fromServer && fromServer.marketHour != null
|
|
49
|
+
? {
|
|
50
|
+
nextStartTime: BigInt(fromServer.marketHour.nextStartTime ?? 0),
|
|
51
|
+
nextEndTime: BigInt(fromServer.marketHour.nextEndTime ?? 0),
|
|
52
|
+
period: BigInt(fromServer.marketHour.period ?? 0),
|
|
53
|
+
roundInterval: BigInt(fromServer.marketHour.roundInterval ?? 0),
|
|
54
|
+
}
|
|
55
|
+
: undefined,
|
|
58
56
|
};
|
|
59
57
|
}
|
|
60
|
-
async
|
|
61
|
-
const url = `${this.serverUrl}/v1/
|
|
58
|
+
async submitTx(signedArkTx, checkpointTxs) {
|
|
59
|
+
const url = `${this.serverUrl}/v1/tx/submit`;
|
|
62
60
|
const response = await fetch(url, {
|
|
63
61
|
method: "POST",
|
|
64
62
|
headers: {
|
|
65
63
|
"Content-Type": "application/json",
|
|
66
64
|
},
|
|
67
65
|
body: JSON.stringify({
|
|
68
|
-
|
|
66
|
+
signedArkTx: signedArkTx,
|
|
67
|
+
checkpointTxs: checkpointTxs,
|
|
69
68
|
}),
|
|
70
69
|
});
|
|
71
70
|
if (!response.ok) {
|
|
@@ -82,140 +81,96 @@ class RestArkProvider {
|
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
const data = await response.json();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const url = `${this.serverUrl}/v1/events`;
|
|
90
|
-
let abortController = new AbortController();
|
|
91
|
-
(async () => {
|
|
92
|
-
while (!abortController.signal.aborted) {
|
|
93
|
-
try {
|
|
94
|
-
const response = await fetch(url, {
|
|
95
|
-
headers: {
|
|
96
|
-
Accept: "application/json",
|
|
97
|
-
},
|
|
98
|
-
signal: abortController.signal,
|
|
99
|
-
});
|
|
100
|
-
if (!response.ok) {
|
|
101
|
-
throw new Error(`Unexpected status ${response.status} when fetching event stream`);
|
|
102
|
-
}
|
|
103
|
-
if (!response.body) {
|
|
104
|
-
throw new Error("Response body is null");
|
|
105
|
-
}
|
|
106
|
-
const reader = response.body.getReader();
|
|
107
|
-
const decoder = new TextDecoder();
|
|
108
|
-
let buffer = "";
|
|
109
|
-
while (!abortController.signal.aborted) {
|
|
110
|
-
const { done, value } = await reader.read();
|
|
111
|
-
if (done)
|
|
112
|
-
break;
|
|
113
|
-
// Append new data to buffer and split by newlines
|
|
114
|
-
buffer += decoder.decode(value, { stream: true });
|
|
115
|
-
const lines = buffer.split("\n");
|
|
116
|
-
// Process all complete lines
|
|
117
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
118
|
-
const line = lines[i].trim();
|
|
119
|
-
if (!line)
|
|
120
|
-
continue;
|
|
121
|
-
try {
|
|
122
|
-
const data = JSON.parse(line);
|
|
123
|
-
callback(data);
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
console.error("Failed to parse event:", err);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
// Keep the last partial line in the buffer
|
|
130
|
-
buffer = lines[lines.length - 1];
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
if (!abortController.signal.aborted) {
|
|
135
|
-
console.error("Event stream error:", error);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
})();
|
|
140
|
-
// Return unsubscribe function
|
|
141
|
-
return () => {
|
|
142
|
-
abortController.abort();
|
|
143
|
-
// Create a new controller for potential future subscriptions
|
|
144
|
-
abortController = new AbortController();
|
|
84
|
+
return {
|
|
85
|
+
arkTxid: data.arkTxid,
|
|
86
|
+
finalArkTx: data.finalArkTx,
|
|
87
|
+
signedCheckpointTxs: data.signedCheckpointTxs,
|
|
145
88
|
};
|
|
146
89
|
}
|
|
147
|
-
async
|
|
148
|
-
const url = `${this.serverUrl}/v1/
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
scripts: input.tapscripts,
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
}
|
|
90
|
+
async finalizeTx(arkTxid, finalCheckpointTxs) {
|
|
91
|
+
const url = `${this.serverUrl}/v1/tx/finalize`;
|
|
92
|
+
const response = await fetch(url, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
arkTxid,
|
|
99
|
+
finalCheckpointTxs,
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
const errorText = await response.text();
|
|
104
|
+
throw new Error(`Failed to finalize offchain transaction: ${errorText}`);
|
|
166
105
|
}
|
|
106
|
+
}
|
|
107
|
+
async registerIntent(intent) {
|
|
108
|
+
const url = `${this.serverUrl}/v1/batch/registerIntent`;
|
|
167
109
|
const response = await fetch(url, {
|
|
168
110
|
method: "POST",
|
|
169
111
|
headers: {
|
|
170
112
|
"Content-Type": "application/json",
|
|
171
113
|
},
|
|
172
114
|
body: JSON.stringify({
|
|
173
|
-
|
|
174
|
-
|
|
115
|
+
intent: {
|
|
116
|
+
signature: intent.signature,
|
|
117
|
+
message: intent.message,
|
|
118
|
+
},
|
|
175
119
|
}),
|
|
176
120
|
});
|
|
177
121
|
if (!response.ok) {
|
|
178
122
|
const errorText = await response.text();
|
|
179
|
-
throw new Error(`Failed to register
|
|
123
|
+
throw new Error(`Failed to register intent: ${errorText}`);
|
|
180
124
|
}
|
|
181
125
|
const data = await response.json();
|
|
182
|
-
return
|
|
126
|
+
return data.intentId;
|
|
183
127
|
}
|
|
184
|
-
async
|
|
185
|
-
const url = `${this.serverUrl}/v1/
|
|
128
|
+
async deleteIntent(intent) {
|
|
129
|
+
const url = `${this.serverUrl}/v1/batch/deleteIntent`;
|
|
186
130
|
const response = await fetch(url, {
|
|
187
131
|
method: "POST",
|
|
188
132
|
headers: {
|
|
189
133
|
"Content-Type": "application/json",
|
|
190
134
|
},
|
|
191
135
|
body: JSON.stringify({
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
amount: output.amount.toString(10),
|
|
196
|
-
})),
|
|
197
|
-
musig2: {
|
|
198
|
-
cosignersPublicKeys,
|
|
199
|
-
signingAll,
|
|
136
|
+
proof: {
|
|
137
|
+
signature: intent.signature,
|
|
138
|
+
message: intent.message,
|
|
200
139
|
},
|
|
201
140
|
}),
|
|
202
141
|
});
|
|
203
142
|
if (!response.ok) {
|
|
204
143
|
const errorText = await response.text();
|
|
205
|
-
throw new Error(`Failed to
|
|
144
|
+
throw new Error(`Failed to delete intent: ${errorText}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async confirmRegistration(intentId) {
|
|
148
|
+
const url = `${this.serverUrl}/v1/batch/ack`;
|
|
149
|
+
const response = await fetch(url, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: {
|
|
152
|
+
"Content-Type": "application/json",
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
intentId,
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
const errorText = await response.text();
|
|
160
|
+
throw new Error(`Failed to confirm registration: ${errorText}`);
|
|
206
161
|
}
|
|
207
162
|
}
|
|
208
|
-
async submitTreeNonces(
|
|
209
|
-
const url = `${this.serverUrl}/v1/
|
|
163
|
+
async submitTreeNonces(batchId, pubkey, nonces) {
|
|
164
|
+
const url = `${this.serverUrl}/v1/batch/tree/submitNonces`;
|
|
210
165
|
const response = await fetch(url, {
|
|
211
166
|
method: "POST",
|
|
212
167
|
headers: {
|
|
213
168
|
"Content-Type": "application/json",
|
|
214
169
|
},
|
|
215
170
|
body: JSON.stringify({
|
|
216
|
-
|
|
171
|
+
batchId,
|
|
217
172
|
pubkey,
|
|
218
|
-
treeNonces:
|
|
173
|
+
treeNonces: encodeMusig2Nonces(nonces),
|
|
219
174
|
}),
|
|
220
175
|
});
|
|
221
176
|
if (!response.ok) {
|
|
@@ -223,17 +178,17 @@ class RestArkProvider {
|
|
|
223
178
|
throw new Error(`Failed to submit tree nonces: ${errorText}`);
|
|
224
179
|
}
|
|
225
180
|
}
|
|
226
|
-
async submitTreeSignatures(
|
|
227
|
-
const url = `${this.serverUrl}/v1/
|
|
181
|
+
async submitTreeSignatures(batchId, pubkey, signatures) {
|
|
182
|
+
const url = `${this.serverUrl}/v1/batch/tree/submitSignatures`;
|
|
228
183
|
const response = await fetch(url, {
|
|
229
184
|
method: "POST",
|
|
230
185
|
headers: {
|
|
231
186
|
"Content-Type": "application/json",
|
|
232
187
|
},
|
|
233
188
|
body: JSON.stringify({
|
|
234
|
-
|
|
189
|
+
batchId,
|
|
235
190
|
pubkey,
|
|
236
|
-
treeSignatures:
|
|
191
|
+
treeSignatures: encodeMusig2Signatures(signatures),
|
|
237
192
|
}),
|
|
238
193
|
});
|
|
239
194
|
if (!response.ok) {
|
|
@@ -241,8 +196,8 @@ class RestArkProvider {
|
|
|
241
196
|
throw new Error(`Failed to submit tree signatures: ${errorText}`);
|
|
242
197
|
}
|
|
243
198
|
}
|
|
244
|
-
async submitSignedForfeitTxs(signedForfeitTxs,
|
|
245
|
-
const url = `${this.serverUrl}/v1/
|
|
199
|
+
async submitSignedForfeitTxs(signedForfeitTxs, signedCommitmentTx) {
|
|
200
|
+
const url = `${this.serverUrl}/v1/batch/submitForfeitTxs`;
|
|
246
201
|
const response = await fetch(url, {
|
|
247
202
|
method: "POST",
|
|
248
203
|
headers: {
|
|
@@ -250,25 +205,21 @@ class RestArkProvider {
|
|
|
250
205
|
},
|
|
251
206
|
body: JSON.stringify({
|
|
252
207
|
signedForfeitTxs: signedForfeitTxs,
|
|
253
|
-
|
|
208
|
+
signedCommitmentTx: signedCommitmentTx,
|
|
254
209
|
}),
|
|
255
210
|
});
|
|
256
211
|
if (!response.ok) {
|
|
257
212
|
throw new Error(`Failed to submit forfeit transactions: ${response.statusText}`);
|
|
258
213
|
}
|
|
259
214
|
}
|
|
260
|
-
async
|
|
261
|
-
const url = `${this.serverUrl}/v1/
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
async *getEventStream(signal) {
|
|
268
|
-
const url = `${this.serverUrl}/v1/events`;
|
|
215
|
+
async *getEventStream(signal, topics) {
|
|
216
|
+
const url = `${this.serverUrl}/v1/batch/events`;
|
|
217
|
+
const queryParams = topics.length > 0
|
|
218
|
+
? `?${topics.map((topic) => `topics=${encodeURIComponent(topic)}`).join("&")}`
|
|
219
|
+
: "";
|
|
269
220
|
while (!signal?.aborted) {
|
|
270
221
|
try {
|
|
271
|
-
const response = await fetch(url, {
|
|
222
|
+
const response = await fetch(url + queryParams, {
|
|
272
223
|
headers: {
|
|
273
224
|
Accept: "application/json",
|
|
274
225
|
},
|
|
@@ -316,22 +267,29 @@ class RestArkProvider {
|
|
|
316
267
|
if (error instanceof Error && error.name === "AbortError") {
|
|
317
268
|
break;
|
|
318
269
|
}
|
|
270
|
+
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
271
|
+
// these timeouts are set by builtin fetch function
|
|
272
|
+
if (isFetchTimeoutError(error)) {
|
|
273
|
+
console.debug("Timeout error ignored");
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
319
276
|
console.error("Event stream error:", error);
|
|
320
277
|
throw error;
|
|
321
278
|
}
|
|
322
279
|
}
|
|
323
280
|
}
|
|
324
|
-
async *
|
|
325
|
-
const url = `${this.serverUrl}/v1/
|
|
326
|
-
while (!
|
|
281
|
+
async *getTransactionsStream(signal) {
|
|
282
|
+
const url = `${this.serverUrl}/v1/txs`;
|
|
283
|
+
while (!signal?.aborted) {
|
|
327
284
|
try {
|
|
328
285
|
const response = await fetch(url, {
|
|
329
286
|
headers: {
|
|
330
287
|
Accept: "application/json",
|
|
331
288
|
},
|
|
289
|
+
signal,
|
|
332
290
|
});
|
|
333
291
|
if (!response.ok) {
|
|
334
|
-
throw new Error(`Unexpected status ${response.status} when
|
|
292
|
+
throw new Error(`Unexpected status ${response.status} when fetching transaction stream`);
|
|
335
293
|
}
|
|
336
294
|
if (!response.body) {
|
|
337
295
|
throw new Error("Response body is null");
|
|
@@ -339,35 +297,33 @@ class RestArkProvider {
|
|
|
339
297
|
const reader = response.body.getReader();
|
|
340
298
|
const decoder = new TextDecoder();
|
|
341
299
|
let buffer = "";
|
|
342
|
-
while (!
|
|
300
|
+
while (!signal?.aborted) {
|
|
343
301
|
const { done, value } = await reader.read();
|
|
344
302
|
if (done) {
|
|
345
303
|
break;
|
|
346
304
|
}
|
|
305
|
+
// Append new data to buffer and split by newlines
|
|
347
306
|
buffer += decoder.decode(value, { stream: true });
|
|
348
307
|
const lines = buffer.split("\n");
|
|
308
|
+
// Process all complete lines
|
|
349
309
|
for (let i = 0; i < lines.length - 1; i++) {
|
|
350
310
|
const line = lines[i].trim();
|
|
351
311
|
if (!line)
|
|
352
312
|
continue;
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
newVtxos: (data.result.newVtxos || []).map(convertVtxo),
|
|
358
|
-
spentVtxos: (data.result.spentVtxos || []).map(convertVtxo),
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
catch (err) {
|
|
363
|
-
console.error("Failed to parse address update:", err);
|
|
364
|
-
throw err;
|
|
313
|
+
const data = JSON.parse(line);
|
|
314
|
+
const txNotification = this.parseTransactionNotification(data.result);
|
|
315
|
+
if (txNotification) {
|
|
316
|
+
yield txNotification;
|
|
365
317
|
}
|
|
366
318
|
}
|
|
319
|
+
// Keep the last partial line in the buffer
|
|
367
320
|
buffer = lines[lines.length - 1];
|
|
368
321
|
}
|
|
369
322
|
}
|
|
370
323
|
catch (error) {
|
|
324
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
371
327
|
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
372
328
|
// these timeouts are set by builtin fetch function
|
|
373
329
|
if (isFetchTimeoutError(error)) {
|
|
@@ -379,185 +335,169 @@ class RestArkProvider {
|
|
|
379
335
|
}
|
|
380
336
|
}
|
|
381
337
|
}
|
|
382
|
-
toConnectorsIndex(connectorsIndex) {
|
|
383
|
-
return new Map(Object.entries(connectorsIndex).map(([key, value]) => [
|
|
384
|
-
key,
|
|
385
|
-
{ txid: value.txid, vout: value.vout },
|
|
386
|
-
]));
|
|
387
|
-
}
|
|
388
|
-
toTxTree(t) {
|
|
389
|
-
// collect the parent txids to determine later if a node is a leaf
|
|
390
|
-
const parentTxids = new Set();
|
|
391
|
-
t.levels.forEach((level) => level.nodes.forEach((node) => {
|
|
392
|
-
if (node.parentTxid) {
|
|
393
|
-
parentTxids.add(node.parentTxid);
|
|
394
|
-
}
|
|
395
|
-
}));
|
|
396
|
-
return new vtxoTree_1.TxTree(t.levels.map((level) => level.nodes.map((node) => ({
|
|
397
|
-
txid: node.txid,
|
|
398
|
-
tx: node.tx,
|
|
399
|
-
parentTxid: node.parentTxid,
|
|
400
|
-
leaf: !parentTxids.has(node.txid),
|
|
401
|
-
}))));
|
|
402
|
-
}
|
|
403
338
|
parseSettlementEvent(data) {
|
|
404
|
-
// Check for
|
|
405
|
-
if (data.
|
|
339
|
+
// Check for BatchStarted event
|
|
340
|
+
if (data.batchStarted) {
|
|
341
|
+
return {
|
|
342
|
+
type: SettlementEventType.BatchStarted,
|
|
343
|
+
id: data.batchStarted.id,
|
|
344
|
+
intentIdHashes: data.batchStarted.intentIdHashes,
|
|
345
|
+
batchExpiry: BigInt(data.batchStarted.batchExpiry),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
// Check for BatchFinalization event
|
|
349
|
+
if (data.batchFinalization) {
|
|
350
|
+
return {
|
|
351
|
+
type: SettlementEventType.BatchFinalization,
|
|
352
|
+
id: data.batchFinalization.id,
|
|
353
|
+
commitmentTx: data.batchFinalization.commitmentTx,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
// Check for BatchFinalized event
|
|
357
|
+
if (data.batchFinalized) {
|
|
358
|
+
return {
|
|
359
|
+
type: SettlementEventType.BatchFinalized,
|
|
360
|
+
id: data.batchFinalized.id,
|
|
361
|
+
commitmentTxid: data.batchFinalized.commitmentTxid,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
// Check for BatchFailed event
|
|
365
|
+
if (data.batchFailed) {
|
|
406
366
|
return {
|
|
407
|
-
type: SettlementEventType.
|
|
408
|
-
id: data.
|
|
409
|
-
|
|
410
|
-
vtxoTree: this.toTxTree(data.roundFinalization.vtxoTree),
|
|
411
|
-
connectors: this.toTxTree(data.roundFinalization.connectors),
|
|
412
|
-
connectorsIndex: this.toConnectorsIndex(data.roundFinalization.connectorsIndex),
|
|
413
|
-
// divide by 1000 to convert to sat/vbyte
|
|
414
|
-
minRelayFeeRate: BigInt(data.roundFinalization.minRelayFeeRate) /
|
|
415
|
-
BigInt(1000),
|
|
367
|
+
type: SettlementEventType.BatchFailed,
|
|
368
|
+
id: data.batchFailed.id,
|
|
369
|
+
reason: data.batchFailed.reason,
|
|
416
370
|
};
|
|
417
371
|
}
|
|
418
|
-
// Check for
|
|
419
|
-
if (data.
|
|
372
|
+
// Check for TreeSigningStarted event
|
|
373
|
+
if (data.treeSigningStarted) {
|
|
420
374
|
return {
|
|
421
|
-
type: SettlementEventType.
|
|
422
|
-
id: data.
|
|
423
|
-
|
|
375
|
+
type: SettlementEventType.TreeSigningStarted,
|
|
376
|
+
id: data.treeSigningStarted.id,
|
|
377
|
+
cosignersPublicKeys: data.treeSigningStarted.cosignersPubkeys,
|
|
378
|
+
unsignedCommitmentTx: data.treeSigningStarted.unsignedCommitmentTx,
|
|
424
379
|
};
|
|
425
380
|
}
|
|
426
|
-
// Check for
|
|
427
|
-
if (data.
|
|
381
|
+
// Check for TreeNoncesAggregated event
|
|
382
|
+
if (data.treeNoncesAggregated) {
|
|
428
383
|
return {
|
|
429
|
-
type: SettlementEventType.
|
|
430
|
-
id: data.
|
|
431
|
-
|
|
384
|
+
type: SettlementEventType.TreeNoncesAggregated,
|
|
385
|
+
id: data.treeNoncesAggregated.id,
|
|
386
|
+
treeNonces: decodeMusig2Nonces(data.treeNoncesAggregated.treeNonces),
|
|
432
387
|
};
|
|
433
388
|
}
|
|
434
|
-
// Check for
|
|
435
|
-
if (data.
|
|
389
|
+
// Check for TreeTx event
|
|
390
|
+
if (data.treeTx) {
|
|
391
|
+
const children = Object.fromEntries(Object.entries(data.treeTx.children).map(([outputIndex, txid]) => {
|
|
392
|
+
return [parseInt(outputIndex), txid];
|
|
393
|
+
}));
|
|
436
394
|
return {
|
|
437
|
-
type: SettlementEventType.
|
|
438
|
-
id: data.
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
395
|
+
type: SettlementEventType.TreeTx,
|
|
396
|
+
id: data.treeTx.id,
|
|
397
|
+
topic: data.treeTx.topic,
|
|
398
|
+
batchIndex: data.treeTx.batchIndex,
|
|
399
|
+
chunk: {
|
|
400
|
+
txid: data.treeTx.txid,
|
|
401
|
+
tx: data.treeTx.tx,
|
|
402
|
+
children,
|
|
403
|
+
},
|
|
442
404
|
};
|
|
443
405
|
}
|
|
444
|
-
|
|
445
|
-
if (data.roundSigningNoncesGenerated) {
|
|
406
|
+
if (data.treeSignature) {
|
|
446
407
|
return {
|
|
447
|
-
type: SettlementEventType.
|
|
448
|
-
id: data.
|
|
449
|
-
|
|
408
|
+
type: SettlementEventType.TreeSignature,
|
|
409
|
+
id: data.treeSignature.id,
|
|
410
|
+
topic: data.treeSignature.topic,
|
|
411
|
+
batchIndex: data.treeSignature.batchIndex,
|
|
412
|
+
txid: data.treeSignature.txid,
|
|
413
|
+
signature: data.treeSignature.signature,
|
|
450
414
|
};
|
|
451
415
|
}
|
|
452
|
-
console.warn("Unknown event
|
|
416
|
+
console.warn("Unknown event type:", data);
|
|
453
417
|
return null;
|
|
454
418
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
totalSize += cell.length;
|
|
419
|
+
parseTransactionNotification(data) {
|
|
420
|
+
if (data.commitmentTx) {
|
|
421
|
+
return {
|
|
422
|
+
commitmentTx: {
|
|
423
|
+
txid: data.commitmentTx.txid,
|
|
424
|
+
tx: data.commitmentTx.tx,
|
|
425
|
+
spentVtxos: data.commitmentTx.spentVtxos.map(mapVtxo),
|
|
426
|
+
spendableVtxos: data.commitmentTx.spendableVtxos.map(mapVtxo),
|
|
427
|
+
checkpointTxs: data.commitmentTx.checkpointTxs,
|
|
428
|
+
},
|
|
429
|
+
};
|
|
467
430
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
// Write row length
|
|
479
|
-
view.setUint32(offset, row.length, true);
|
|
480
|
-
offset += 4;
|
|
481
|
-
// Write each cell
|
|
482
|
-
for (const cell of row) {
|
|
483
|
-
const notNil = cell.length > 0;
|
|
484
|
-
view.setInt8(offset, notNil ? 1 : 0);
|
|
485
|
-
offset += 1;
|
|
486
|
-
if (!notNil) {
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
new Uint8Array(buffer).set(cell, offset);
|
|
490
|
-
offset += cell.length;
|
|
431
|
+
if (data.arkTx) {
|
|
432
|
+
return {
|
|
433
|
+
arkTx: {
|
|
434
|
+
txid: data.arkTx.txid,
|
|
435
|
+
tx: data.arkTx.tx,
|
|
436
|
+
spentVtxos: data.arkTx.spentVtxos.map(mapVtxo),
|
|
437
|
+
spendableVtxos: data.arkTx.spendableVtxos.map(mapVtxo),
|
|
438
|
+
checkpointTxs: data.arkTx.checkpointTxs,
|
|
439
|
+
},
|
|
440
|
+
};
|
|
491
441
|
}
|
|
442
|
+
console.warn("Unknown transaction notification type:", data);
|
|
443
|
+
return null;
|
|
492
444
|
}
|
|
493
|
-
return new Uint8Array(buffer);
|
|
494
445
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const numRows = view.getUint32(offset, true); // true for little-endian
|
|
501
|
-
offset += 4;
|
|
502
|
-
// Initialize result matrix
|
|
503
|
-
const result = [];
|
|
504
|
-
// Read each row
|
|
505
|
-
for (let i = 0; i < numRows; i++) {
|
|
506
|
-
// Read row length
|
|
507
|
-
const rowLength = view.getUint32(offset, true);
|
|
508
|
-
offset += 4;
|
|
509
|
-
const row = [];
|
|
510
|
-
// Read each cell in the row
|
|
511
|
-
for (let j = 0; j < rowLength; j++) {
|
|
512
|
-
const notNil = view.getUint8(offset) === 1;
|
|
513
|
-
offset += 1;
|
|
514
|
-
if (notNil) {
|
|
515
|
-
const cell = new Uint8Array(matrix.buffer, matrix.byteOffset + offset, cellLength);
|
|
516
|
-
row.push(new Uint8Array(cell));
|
|
517
|
-
offset += cellLength;
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
row.push(new Uint8Array());
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
result.push(row);
|
|
446
|
+
exports.RestArkProvider = RestArkProvider;
|
|
447
|
+
function encodeMusig2Nonces(nonces) {
|
|
448
|
+
const noncesObject = {};
|
|
449
|
+
for (const [txid, nonce] of nonces) {
|
|
450
|
+
noncesObject[txid] = base_1.hex.encode(nonce.pubNonce);
|
|
524
451
|
}
|
|
525
|
-
return
|
|
452
|
+
return JSON.stringify(noncesObject);
|
|
526
453
|
}
|
|
527
|
-
function
|
|
528
|
-
const
|
|
529
|
-
|
|
454
|
+
function encodeMusig2Signatures(signatures) {
|
|
455
|
+
const sigObject = {};
|
|
456
|
+
for (const [txid, sig] of signatures) {
|
|
457
|
+
sigObject[txid] = base_1.hex.encode(sig.encode());
|
|
458
|
+
}
|
|
459
|
+
return JSON.stringify(sigObject);
|
|
530
460
|
}
|
|
531
|
-
function
|
|
532
|
-
|
|
461
|
+
function decodeMusig2Nonces(str) {
|
|
462
|
+
const noncesObject = JSON.parse(str);
|
|
463
|
+
return new Map(Object.entries(noncesObject).map(([txid, nonce]) => {
|
|
464
|
+
if (typeof nonce !== "string") {
|
|
465
|
+
throw new Error("invalid nonce");
|
|
466
|
+
}
|
|
467
|
+
return [txid, { pubNonce: base_1.hex.decode(nonce) }];
|
|
468
|
+
}));
|
|
533
469
|
}
|
|
534
|
-
function
|
|
535
|
-
|
|
470
|
+
function isFetchTimeoutError(err) {
|
|
471
|
+
const checkError = (error) => {
|
|
472
|
+
if (!(error instanceof Error))
|
|
473
|
+
return false;
|
|
474
|
+
// TODO: get something more robust than this
|
|
475
|
+
const isCloudflare524 = error.name === "TypeError" && error.message === "Failed to fetch";
|
|
476
|
+
return (isCloudflare524 ||
|
|
477
|
+
error.name === "HeadersTimeoutError" ||
|
|
478
|
+
error.name === "BodyTimeoutError" ||
|
|
479
|
+
error.code === "UND_ERR_HEADERS_TIMEOUT" ||
|
|
480
|
+
error.code === "UND_ERR_BODY_TIMEOUT");
|
|
481
|
+
};
|
|
482
|
+
return checkError(err) || checkError(err.cause);
|
|
536
483
|
}
|
|
537
|
-
function
|
|
484
|
+
function mapVtxo(vtxo) {
|
|
538
485
|
return {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
status: {
|
|
543
|
-
confirmed: !!vtxo.roundTxid,
|
|
544
|
-
},
|
|
545
|
-
virtualStatus: {
|
|
546
|
-
state: vtxo.isPending ? "pending" : "settled",
|
|
547
|
-
batchTxID: vtxo.roundTxid,
|
|
548
|
-
batchExpiry: vtxo.expireAt ? Number(vtxo.expireAt) : undefined,
|
|
486
|
+
outpoint: {
|
|
487
|
+
txid: vtxo.outpoint.txid,
|
|
488
|
+
vout: vtxo.outpoint.vout,
|
|
549
489
|
},
|
|
490
|
+
amount: vtxo.amount,
|
|
491
|
+
script: vtxo.script,
|
|
492
|
+
createdAt: vtxo.createdAt,
|
|
493
|
+
expiresAt: vtxo.expiresAt,
|
|
494
|
+
commitmentTxids: vtxo.commitmentTxids,
|
|
495
|
+
isPreconfirmed: vtxo.isPreconfirmed,
|
|
496
|
+
isSwept: vtxo.isSwept,
|
|
497
|
+
isUnrolled: vtxo.isUnrolled,
|
|
498
|
+
isSpent: vtxo.isSpent,
|
|
550
499
|
spentBy: vtxo.spentBy,
|
|
551
|
-
|
|
500
|
+
settledBy: vtxo.settledBy,
|
|
501
|
+
arkTxid: vtxo.arkTxid,
|
|
552
502
|
};
|
|
553
503
|
}
|
|
554
|
-
function isFetchTimeoutError(err) {
|
|
555
|
-
const checkError = (error) => {
|
|
556
|
-
return (error instanceof Error &&
|
|
557
|
-
(error.name === "HeadersTimeoutError" ||
|
|
558
|
-
error.name === "BodyTimeoutError" ||
|
|
559
|
-
error.code === "UND_ERR_HEADERS_TIMEOUT" ||
|
|
560
|
-
error.code === "UND_ERR_BODY_TIMEOUT"));
|
|
561
|
-
};
|
|
562
|
-
return checkError(err) || checkError(err.cause);
|
|
563
|
-
}
|