@arkade-os/sdk 0.4.0-next.1 → 0.4.0-next.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/{asset → extension/asset}/index.js +1 -2
- package/dist/cjs/extension/asset/packet.js +111 -0
- package/dist/cjs/{asset → extension/asset}/types.js +1 -4
- package/dist/cjs/extension/index.js +254 -0
- package/dist/cjs/extension/packet.js +20 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/providers/ark.js +52 -37
- package/dist/cjs/providers/indexer.js +1 -1
- package/dist/cjs/providers/utils.js +39 -29
- package/dist/cjs/wallet/asset-manager.js +9 -17
- package/dist/cjs/wallet/asset.js +1 -1
- package/dist/cjs/wallet/serviceWorker/wallet.js +9 -1
- package/dist/cjs/wallet/validation.js +6 -2
- package/dist/cjs/wallet/wallet.js +5 -4
- package/dist/cjs/worker/messageBus.js +22 -2
- package/dist/esm/{asset → extension/asset}/index.js +1 -1
- package/dist/esm/extension/asset/packet.js +107 -0
- package/dist/esm/{asset → extension/asset}/types.js +0 -3
- package/dist/esm/extension/index.js +248 -0
- package/dist/esm/extension/packet.js +16 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/providers/ark.js +52 -37
- package/dist/esm/providers/indexer.js +1 -1
- package/dist/esm/providers/utils.js +39 -29
- package/dist/esm/wallet/asset-manager.js +9 -17
- package/dist/esm/wallet/asset.js +1 -1
- package/dist/esm/wallet/serviceWorker/wallet.js +9 -1
- package/dist/esm/wallet/validation.js +6 -2
- package/dist/esm/wallet/wallet.js +5 -4
- package/dist/esm/worker/messageBus.js +22 -2
- package/dist/types/{asset → extension/asset}/index.d.ts +1 -1
- package/dist/types/extension/asset/packet.d.ts +38 -0
- package/dist/types/{asset → extension/asset}/types.d.ts +0 -2
- package/dist/types/extension/index.d.ts +56 -0
- package/dist/types/extension/packet.d.ts +21 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/providers/utils.d.ts +6 -0
- package/dist/types/wallet/asset-manager.d.ts +4 -13
- package/dist/types/wallet/asset.d.ts +1 -1
- package/package.json +1 -1
- package/dist/cjs/asset/packet.js +0 -164
- package/dist/esm/asset/packet.js +0 -159
- package/dist/types/asset/packet.d.ts +0 -27
- /package/dist/cjs/{asset → extension/asset}/assetGroup.js +0 -0
- /package/dist/cjs/{asset → extension/asset}/assetId.js +0 -0
- /package/dist/cjs/{asset → extension/asset}/assetInput.js +0 -0
- /package/dist/cjs/{asset → extension/asset}/assetOutput.js +0 -0
- /package/dist/cjs/{asset → extension/asset}/assetRef.js +0 -0
- /package/dist/cjs/{asset → extension/asset}/metadata.js +0 -0
- /package/dist/cjs/{asset → extension/asset}/utils.js +0 -0
- /package/dist/esm/{asset → extension/asset}/assetGroup.js +0 -0
- /package/dist/esm/{asset → extension/asset}/assetId.js +0 -0
- /package/dist/esm/{asset → extension/asset}/assetInput.js +0 -0
- /package/dist/esm/{asset → extension/asset}/assetOutput.js +0 -0
- /package/dist/esm/{asset → extension/asset}/assetRef.js +0 -0
- /package/dist/esm/{asset → extension/asset}/metadata.js +0 -0
- /package/dist/esm/{asset → extension/asset}/utils.js +0 -0
- /package/dist/types/{asset → extension/asset}/assetGroup.d.ts +0 -0
- /package/dist/types/{asset → extension/asset}/assetId.d.ts +0 -0
- /package/dist/types/{asset → extension/asset}/assetInput.d.ts +0 -0
- /package/dist/types/{asset → extension/asset}/assetOutput.d.ts +0 -0
- /package/dist/types/{asset → extension/asset}/assetRef.d.ts +0 -0
- /package/dist/types/{asset → extension/asset}/metadata.d.ts +0 -0
- /package/dist/types/{asset → extension/asset}/utils.d.ts +0 -0
|
@@ -221,54 +221,69 @@ export class RestArkProvider {
|
|
|
221
221
|
handleError(errorText, `Failed to submit forfeit transactions: ${response.statusText}`);
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
|
|
224
|
+
getEventStream(signal, topics) {
|
|
225
225
|
const url = `${this.serverUrl}/v1/batch/events`;
|
|
226
226
|
const queryParams = topics.length > 0
|
|
227
227
|
? `?${topics.map((topic) => `topics=${encodeURIComponent(topic)}`).join("&")}`
|
|
228
228
|
: "";
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
229
|
+
// Create first EventSource eagerly so events are buffered
|
|
230
|
+
// before the caller starts iterating, preventing race conditions
|
|
231
|
+
// where the server emits events before iteration begins.
|
|
232
|
+
const eagerEventSource = new EventSource(url + queryParams);
|
|
233
|
+
const eagerIterator = eventSourceIterator(eagerEventSource);
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
235
|
+
const self = this;
|
|
236
|
+
return (async function* () {
|
|
237
|
+
let firstIteration = true;
|
|
238
|
+
while (!signal?.aborted) {
|
|
239
|
+
const eventSource = firstIteration
|
|
240
|
+
? eagerEventSource
|
|
241
|
+
: new EventSource(url + queryParams);
|
|
242
|
+
const iterator = firstIteration
|
|
243
|
+
? eagerIterator
|
|
244
|
+
: eventSourceIterator(eventSource);
|
|
245
|
+
firstIteration = false;
|
|
237
246
|
try {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (
|
|
245
|
-
|
|
247
|
+
const abortHandler = () => {
|
|
248
|
+
eventSource.close();
|
|
249
|
+
};
|
|
250
|
+
signal?.addEventListener("abort", abortHandler);
|
|
251
|
+
try {
|
|
252
|
+
for await (const event of iterator) {
|
|
253
|
+
if (signal?.aborted)
|
|
254
|
+
break;
|
|
255
|
+
try {
|
|
256
|
+
const data = JSON.parse(event.data);
|
|
257
|
+
const settlementEvent = self.parseSettlementEvent(data);
|
|
258
|
+
if (settlementEvent) {
|
|
259
|
+
yield settlementEvent;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
console.error("Failed to parse event:", err);
|
|
264
|
+
throw err;
|
|
246
265
|
}
|
|
247
|
-
}
|
|
248
|
-
catch (err) {
|
|
249
|
-
console.error("Failed to parse event:", err);
|
|
250
|
-
throw err;
|
|
251
266
|
}
|
|
252
267
|
}
|
|
268
|
+
finally {
|
|
269
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
270
|
+
eventSource.close();
|
|
271
|
+
}
|
|
253
272
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
console.debug("Timeout error ignored");
|
|
266
|
-
continue;
|
|
273
|
+
catch (error) {
|
|
274
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
278
|
+
if (isFetchTimeoutError(error)) {
|
|
279
|
+
console.debug("Timeout error ignored");
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
console.error("Event stream error:", error);
|
|
283
|
+
throw error;
|
|
267
284
|
}
|
|
268
|
-
console.error("Event stream error:", error);
|
|
269
|
-
throw error;
|
|
270
285
|
}
|
|
271
|
-
}
|
|
286
|
+
})();
|
|
272
287
|
}
|
|
273
288
|
async *getTransactionsStream(signal) {
|
|
274
289
|
const url = `${this.serverUrl}/v1/txs`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hex } from "@scure/base";
|
|
2
2
|
import { isFetchTimeoutError } from './ark.js';
|
|
3
3
|
import { eventSourceIterator } from './utils.js';
|
|
4
|
-
import { MetadataList } from '../asset/index.js';
|
|
4
|
+
import { MetadataList } from '../extension/asset/index.js';
|
|
5
5
|
export var IndexerTxType;
|
|
6
6
|
(function (IndexerTxType) {
|
|
7
7
|
IndexerTxType[IndexerTxType["INDEXER_TX_TYPE_UNSPECIFIED"] = 0] = "INDEXER_TX_TYPE_UNSPECIFIED";
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Creates an async iterator over EventSource messages that attaches listeners
|
|
3
|
+
* eagerly (at call time) rather than lazily (at first .next() call).
|
|
4
|
+
* This ensures events are buffered immediately, preventing race conditions
|
|
5
|
+
* where events arrive before iteration begins.
|
|
6
|
+
*/
|
|
7
|
+
export function eventSourceIterator(eventSource) {
|
|
2
8
|
const messageQueue = [];
|
|
3
9
|
const errorQueue = [];
|
|
4
10
|
let messageResolve = null;
|
|
@@ -23,36 +29,40 @@ export async function* eventSourceIterator(eventSource) {
|
|
|
23
29
|
errorQueue.push(error);
|
|
24
30
|
}
|
|
25
31
|
};
|
|
32
|
+
// Attach listeners immediately so events are buffered
|
|
33
|
+
// even before the caller starts iterating
|
|
26
34
|
eventSource.addEventListener("message", messageHandler);
|
|
27
35
|
eventSource.addEventListener("error", errorHandler);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
return (async function* () {
|
|
37
|
+
try {
|
|
38
|
+
while (true) {
|
|
39
|
+
// if we have queued messages, yield the first one, remove it from the queue
|
|
40
|
+
if (messageQueue.length > 0) {
|
|
41
|
+
yield messageQueue.shift();
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// if we have queued errors, throw the first one, remove it from the queue
|
|
45
|
+
if (errorQueue.length > 0) {
|
|
46
|
+
const error = errorQueue.shift();
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
// wait for the next message or error
|
|
50
|
+
const result = await new Promise((resolve, reject) => {
|
|
51
|
+
messageResolve = resolve;
|
|
52
|
+
errorResolve = reject;
|
|
53
|
+
}).finally(() => {
|
|
54
|
+
messageResolve = null;
|
|
55
|
+
errorResolve = null;
|
|
56
|
+
});
|
|
57
|
+
if (result) {
|
|
58
|
+
yield result;
|
|
59
|
+
}
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
62
|
+
finally {
|
|
63
|
+
// clean up
|
|
64
|
+
eventSource.removeEventListener("message", messageHandler);
|
|
65
|
+
eventSource.removeEventListener("error", errorHandler);
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
58
68
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { AssetGroup, AssetId, AssetInput, AssetOutput, AssetRef, Metadata, Packet, } from '../asset/index.js';
|
|
1
|
+
import { AssetGroup, AssetId, AssetInput, AssetOutput, AssetRef, Metadata, Packet, } from '../extension/asset/index.js';
|
|
2
2
|
import { ArkAddress } from '../script/address.js';
|
|
3
3
|
import { selectedCoinsToAssetInputs, selectCoinsWithAsset } from './asset.js';
|
|
4
|
+
import { Extension } from '../extension/index.js';
|
|
4
5
|
import { selectVirtualCoins } from './wallet.js';
|
|
5
6
|
export class ReadonlyAssetManager {
|
|
6
7
|
constructor(indexer) {
|
|
@@ -20,30 +21,21 @@ export class AssetManager extends ReadonlyAssetManager {
|
|
|
20
21
|
* Issue a new asset.
|
|
21
22
|
* @param params - Parameters for asset issuance
|
|
22
23
|
* @param params.amount - Amount of asset units to issue
|
|
23
|
-
* @param params.
|
|
24
|
+
* @param params.controlAssetId - Optional control asset ID (for reissuable assets)
|
|
24
25
|
* @param params.metadata - Optional metadata to attach to the asset
|
|
25
26
|
* @returns Promise resolving to the ark transaction ID and asset ID
|
|
26
27
|
*
|
|
27
28
|
* @example
|
|
28
29
|
* ```typescript
|
|
29
30
|
* // Issue a simple non-reissuable asset
|
|
30
|
-
* const result = await wallet.
|
|
31
|
-
* console.log('Asset ID:', result.assetId);
|
|
32
|
-
*
|
|
33
|
-
* // Issue a reissuable asset with a new control asset
|
|
34
|
-
* const result = await wallet.issueAsset({
|
|
35
|
-
* amount: 1000,
|
|
36
|
-
* controlAsset: 1 // creates new control asset with amount 1
|
|
37
|
-
* });
|
|
38
|
-
* console.log('Control Asset ID:', result.controlAssetId);
|
|
31
|
+
* const result = await wallet.assetManager.issue({ amount: 1000 });
|
|
39
32
|
* console.log('Asset ID:', result.assetId);
|
|
40
33
|
*
|
|
41
34
|
* // Issue a reissuable asset with an existing control asset
|
|
42
|
-
* const result = await wallet.
|
|
35
|
+
* const result = await wallet.assetManager.issue({
|
|
43
36
|
* amount: 1000,
|
|
44
|
-
*
|
|
37
|
+
* controlAssetId: 'existingControlAssetId'
|
|
45
38
|
* });
|
|
46
|
-
* console.log('Control Asset ID:', result.controlAssetId);
|
|
47
39
|
* console.log('Asset ID:', result.assetId);
|
|
48
40
|
* ```
|
|
49
41
|
*/
|
|
@@ -101,7 +93,7 @@ export class AssetManager extends ReadonlyAssetManager {
|
|
|
101
93
|
script: outputAddress.pkScript,
|
|
102
94
|
amount: BigInt(totalBtcSelected),
|
|
103
95
|
},
|
|
104
|
-
Packet.create(groups).txOut(),
|
|
96
|
+
Extension.create([Packet.create(groups)]).txOut(),
|
|
105
97
|
];
|
|
106
98
|
const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(coinSelection.inputs, outputs);
|
|
107
99
|
return {
|
|
@@ -213,7 +205,7 @@ export class AssetManager extends ReadonlyAssetManager {
|
|
|
213
205
|
script: outputAddress.pkScript,
|
|
214
206
|
amount: BigInt(totalBtcSelected),
|
|
215
207
|
},
|
|
216
|
-
Packet.create(groups).txOut(),
|
|
208
|
+
Extension.create([Packet.create(groups)]).txOut(),
|
|
217
209
|
];
|
|
218
210
|
const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
|
|
219
211
|
return arkTxid;
|
|
@@ -297,7 +289,7 @@ export class AssetManager extends ReadonlyAssetManager {
|
|
|
297
289
|
script: outputAddress.pkScript,
|
|
298
290
|
amount: BigInt(totalBtcSelected),
|
|
299
291
|
},
|
|
300
|
-
Packet.create(groups).txOut(),
|
|
292
|
+
Extension.create([Packet.create(groups)]).txOut(),
|
|
301
293
|
];
|
|
302
294
|
const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
|
|
303
295
|
return arkTxid;
|
package/dist/esm/wallet/asset.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AssetGroup, AssetId, AssetInput, AssetOutput, Packet, } from '../asset/index.js';
|
|
1
|
+
import { AssetGroup, AssetId, AssetInput, AssetOutput, Packet, } from '../extension/asset/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Creates an asset packet from asset inputs and receivers.
|
|
4
4
|
* Groups inputs and outputs by asset ID and creates the Packet object
|
|
@@ -166,12 +166,20 @@ export class ServiceWorkerReadonlyWallet {
|
|
|
166
166
|
// send a message and wait for a response
|
|
167
167
|
async sendMessage(request) {
|
|
168
168
|
return new Promise((resolve, reject) => {
|
|
169
|
+
const cleanup = () => {
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
172
|
+
};
|
|
173
|
+
const timeoutId = setTimeout(() => {
|
|
174
|
+
cleanup();
|
|
175
|
+
reject(new Error(`Service worker message timed out (${request.type})`));
|
|
176
|
+
}, 30000);
|
|
169
177
|
const messageHandler = (event) => {
|
|
170
178
|
const response = event.data;
|
|
171
179
|
if (request.id !== response.id) {
|
|
172
180
|
return;
|
|
173
181
|
}
|
|
174
|
-
|
|
182
|
+
cleanup();
|
|
175
183
|
if (response.error) {
|
|
176
184
|
reject(response.error);
|
|
177
185
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { equalBytes } from "@scure/btc-signer/utils.js";
|
|
2
2
|
import { ArkAddress } from '../script/address.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Extension } from '../extension/index.js';
|
|
4
4
|
import { Address, OutScript } from "@scure/btc-signer";
|
|
5
5
|
export const ErrOffchainOutputNotFound = (address) => new Error(`offchain send output not found: ${address}`);
|
|
6
6
|
export const ErrInvalidAssetOutputAmount = (got, want, assetId) => new Error(`invalid asset output amount for ${assetId}: got ${got}, want ${want}`);
|
|
@@ -113,7 +113,11 @@ function validateOffchainRecipient(leaves, arkAddress, recipient, usedOutputs //
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
function validateAssetOutputs(leafTx, outputIndex, expectedAssets) {
|
|
116
|
-
const
|
|
116
|
+
const ext = Extension.fromTx(leafTx);
|
|
117
|
+
const assetPacket = ext.getAssetPacket();
|
|
118
|
+
if (!assetPacket) {
|
|
119
|
+
throw new Error("no asset packet found in extension");
|
|
120
|
+
}
|
|
117
121
|
for (const { assetId, amount } of expectedAssets) {
|
|
118
122
|
validateAssetGroupOutput(assetPacket, outputIndex, assetId, amount);
|
|
119
123
|
}
|
|
@@ -25,6 +25,7 @@ import { Batch } from './batch.js';
|
|
|
25
25
|
import { Estimator } from '../arkfee/index.js';
|
|
26
26
|
import { buildTransactionHistory } from '../utils/transactionHistory.js';
|
|
27
27
|
import { AssetManager, ReadonlyAssetManager } from './asset-manager.js';
|
|
28
|
+
import { Extension } from '../extension/index.js';
|
|
28
29
|
import { DelegateVtxo } from '../script/delegate.js';
|
|
29
30
|
import { DelegatorManagerImpl } from './delegator.js';
|
|
30
31
|
import { IndexedDBContractRepository, IndexedDBWalletRepository, } from '../repositories/index.js';
|
|
@@ -912,7 +913,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
912
913
|
}));
|
|
913
914
|
if (outputAssets && outputAssets.length > 0) {
|
|
914
915
|
const assetPacket = createAssetPacket(assetInputs, recipients);
|
|
915
|
-
outputs.push(assetPacket.txOut());
|
|
916
|
+
outputs.push(Extension.create([assetPacket]).txOut());
|
|
916
917
|
}
|
|
917
918
|
// session holds the state of the musig2 signing process of the vtxo tree
|
|
918
919
|
let session;
|
|
@@ -925,15 +926,15 @@ export class Wallet extends ReadonlyWallet {
|
|
|
925
926
|
this.makeRegisterIntentSignature(params.inputs, outputs, onchainOutputIndexes, signingPublicKeys),
|
|
926
927
|
this.makeDeleteIntentSignature(params.inputs),
|
|
927
928
|
]);
|
|
928
|
-
const intentId = await this.safeRegisterIntent(intent);
|
|
929
929
|
const topics = [
|
|
930
930
|
...signingPublicKeys,
|
|
931
931
|
...params.inputs.map((input) => `${input.txid}:${input.vout}`),
|
|
932
932
|
];
|
|
933
|
-
const handler = this.createBatchHandler(intentId, params.inputs, recipients, session);
|
|
934
933
|
const abortController = new AbortController();
|
|
935
934
|
try {
|
|
936
935
|
const stream = this.arkProvider.getEventStream(abortController.signal, topics);
|
|
936
|
+
const intentId = await this.safeRegisterIntent(intent);
|
|
937
|
+
const handler = this.createBatchHandler(intentId, params.inputs, recipients, session);
|
|
937
938
|
const commitmentTxid = await Batch.join(stream, handler, {
|
|
938
939
|
abortController,
|
|
939
940
|
skipVtxoTreeSigning: !hasOffchainOutputs,
|
|
@@ -1398,7 +1399,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1398
1399
|
recipients.some((r) => r.assets && r.assets.length > 0);
|
|
1399
1400
|
if (hasAssets) {
|
|
1400
1401
|
const assetPacket = createAssetPacket(assetInputs, recipients, changeReceiver);
|
|
1401
|
-
outputs.push(assetPacket.txOut());
|
|
1402
|
+
outputs.push(Extension.create([assetPacket]).txOut());
|
|
1402
1403
|
}
|
|
1403
1404
|
const sentAmount = recipients.reduce((sum, r) => sum + r.amount, 0);
|
|
1404
1405
|
const { arkTxid, signedCheckpointTxs } = await this.buildAndSubmitOffchainTx(selectedCoins, outputs);
|
|
@@ -104,8 +104,19 @@ export class MessageBus {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
async waitForInit(config) {
|
|
107
|
-
if (this.initialized)
|
|
108
|
-
|
|
107
|
+
if (this.initialized) {
|
|
108
|
+
// Stop existing handlers before re-initializing.
|
|
109
|
+
// This handles the case where CLEAR was called, which nullifies
|
|
110
|
+
// handler state (readonlyWallet, etc.) without resetting the
|
|
111
|
+
// initialized flag. Without this, handlers never get start()
|
|
112
|
+
// called again and all messages fail with "not initialized".
|
|
113
|
+
//
|
|
114
|
+
// Clear the flag first so onMessage() rejects incoming messages
|
|
115
|
+
// during the stop/start window instead of routing them to
|
|
116
|
+
// half-reset handlers. Restored to true after start() completes.
|
|
117
|
+
this.initialized = false;
|
|
118
|
+
await Promise.all(Array.from(this.handlers.values()).map((h) => h.stop().catch(() => { })));
|
|
119
|
+
}
|
|
109
120
|
const services = await this.buildServicesFn(config);
|
|
110
121
|
// Start all handlers
|
|
111
122
|
for (const updater of this.handlers.values()) {
|
|
@@ -165,6 +176,15 @@ export class MessageBus {
|
|
|
165
176
|
if (!this.initialized) {
|
|
166
177
|
if (this.debug)
|
|
167
178
|
console.warn("Event received before initialization, dropping", event.data);
|
|
179
|
+
// Send error response so the caller's promise rejects instead of
|
|
180
|
+
// hanging forever. This happens when the browser kills and restarts
|
|
181
|
+
// the service worker — the new instance has initialized=false and
|
|
182
|
+
// messages arrive before INITIALIZE_MESSAGE_BUS is re-sent.
|
|
183
|
+
event.source?.postMessage({
|
|
184
|
+
id,
|
|
185
|
+
tag: tag ?? "unknown",
|
|
186
|
+
error: new Error("MessageBus not initialized"),
|
|
187
|
+
});
|
|
168
188
|
return;
|
|
169
189
|
}
|
|
170
190
|
if (!id || !tag) {
|
|
@@ -5,4 +5,4 @@ export { AssetInput, AssetInputs } from "./assetInput";
|
|
|
5
5
|
export { AssetOutput, AssetOutputs } from "./assetOutput";
|
|
6
6
|
export { Metadata, MetadataList } from "./metadata";
|
|
7
7
|
export { AssetGroup } from "./assetGroup";
|
|
8
|
-
export { Packet
|
|
8
|
+
export { Packet } from "./packet";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ExtensionPacket } from "../packet";
|
|
2
|
+
import { AssetGroup } from "./assetGroup";
|
|
3
|
+
/**
|
|
4
|
+
* Packet represents a collection of asset groups.
|
|
5
|
+
* It encodes/decodes as raw bytes only — OP_RETURN framing is handled by the Extension module.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Packet implements ExtensionPacket {
|
|
8
|
+
readonly groups: AssetGroup[];
|
|
9
|
+
/** PACKET_TYPE is the 1-byte TLV type tag used in the Extension envelope. */
|
|
10
|
+
static readonly PACKET_TYPE = 0;
|
|
11
|
+
private constructor();
|
|
12
|
+
static create(groups: AssetGroup[]): Packet;
|
|
13
|
+
/**
|
|
14
|
+
* fromBytes parses a Packet from raw bytes.
|
|
15
|
+
*/
|
|
16
|
+
static fromBytes(buf: Uint8Array): Packet;
|
|
17
|
+
/**
|
|
18
|
+
* fromString parses a Packet from a raw hex string (not an OP_RETURN script).
|
|
19
|
+
*/
|
|
20
|
+
static fromString(s: string): Packet;
|
|
21
|
+
/**
|
|
22
|
+
* type returns the TLV packet type tag. Implements ExtensionPacket interface.
|
|
23
|
+
*/
|
|
24
|
+
type(): number;
|
|
25
|
+
leafTxPacket(intentTxid: Uint8Array): Packet;
|
|
26
|
+
/**
|
|
27
|
+
* serialize encodes the packet as raw bytes (varint group count + group data).
|
|
28
|
+
* Does NOT include OP_RETURN, ARK magic, or TLV type/length — those are
|
|
29
|
+
* added by the Extension module.
|
|
30
|
+
*/
|
|
31
|
+
serialize(): Uint8Array;
|
|
32
|
+
/**
|
|
33
|
+
* toString returns the hex-encoded raw packet bytes.
|
|
34
|
+
*/
|
|
35
|
+
toString(): string;
|
|
36
|
+
validate(): void;
|
|
37
|
+
private static fromReader;
|
|
38
|
+
}
|
|
@@ -14,5 +14,3 @@ export declare enum AssetRefType {
|
|
|
14
14
|
export declare const MASK_ASSET_ID = 1;
|
|
15
15
|
export declare const MASK_CONTROL_ASSET = 2;
|
|
16
16
|
export declare const MASK_METADATA = 4;
|
|
17
|
-
export declare const ARKADE_MAGIC: Uint8Array<ArrayBuffer>;
|
|
18
|
-
export declare const MARKER_ASSET_PAYLOAD = 0;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Packet } from "./asset/packet";
|
|
2
|
+
import { ExtensionPacket } from "./packet";
|
|
3
|
+
import type { TransactionOutput } from "@scure/btc-signer/psbt";
|
|
4
|
+
import type { Transaction } from "../utils/transaction";
|
|
5
|
+
export type { ExtensionPacket } from "./packet";
|
|
6
|
+
export { UnknownPacket } from "./packet";
|
|
7
|
+
/**
|
|
8
|
+
* ArkadeMagic is the 3-byte magic prefix ("ARK") that identifies an OP_RETURN
|
|
9
|
+
* output as an ark extension blob.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ARKADE_MAGIC: Uint8Array<ArrayBuffer>;
|
|
12
|
+
/**
|
|
13
|
+
* ErrExtensionNotFound is thrown when no extension output is found in a transaction.
|
|
14
|
+
*/
|
|
15
|
+
export declare class ExtensionNotFoundError extends Error {
|
|
16
|
+
constructor();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Extension is a set of typed packets encoded in an OP_RETURN output.
|
|
20
|
+
*
|
|
21
|
+
* Wire format:
|
|
22
|
+
* OP_RETURN | <push> | ARK(3B) | [type(1B) | varint_len | data]...
|
|
23
|
+
*/
|
|
24
|
+
export declare class Extension {
|
|
25
|
+
private readonly packets;
|
|
26
|
+
private constructor();
|
|
27
|
+
static create(packets: ExtensionPacket[]): Extension;
|
|
28
|
+
/**
|
|
29
|
+
* isExtension returns true if the script is an OP_RETURN whose push data
|
|
30
|
+
* begins with the ARK magic bytes.
|
|
31
|
+
*/
|
|
32
|
+
static isExtension(script: Uint8Array): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* fromBytes parses an Extension from a raw OP_RETURN script.
|
|
35
|
+
*/
|
|
36
|
+
static fromBytes(script: Uint8Array): Extension;
|
|
37
|
+
/**
|
|
38
|
+
* fromTx searches the transaction outputs for an extension blob and parses it.
|
|
39
|
+
* Throws ExtensionNotFoundError if none is found.
|
|
40
|
+
*/
|
|
41
|
+
static fromTx(tx: Transaction): Extension;
|
|
42
|
+
/**
|
|
43
|
+
* serialize encodes the extension as an OP_RETURN script.
|
|
44
|
+
*
|
|
45
|
+
* Layout: OP_RETURN | <push> | ARK | [type | varint_len | data]...
|
|
46
|
+
*/
|
|
47
|
+
serialize(): Uint8Array;
|
|
48
|
+
/**
|
|
49
|
+
* txOut returns the extension as a zero-value OP_RETURN transaction output.
|
|
50
|
+
*/
|
|
51
|
+
txOut(): Required<Pick<TransactionOutput, "script" | "amount">>;
|
|
52
|
+
/**
|
|
53
|
+
* getAssetPacket returns the embedded Packet, or null if not present.
|
|
54
|
+
*/
|
|
55
|
+
getAssetPacket(): Packet | null;
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExtensionPacket is the interface that all extension packets must implement.
|
|
3
|
+
* It mirrors the Go extension.Packet interface.
|
|
4
|
+
*/
|
|
5
|
+
export interface ExtensionPacket {
|
|
6
|
+
/** type returns the 1-byte packet type tag */
|
|
7
|
+
type(): number;
|
|
8
|
+
/** serialize returns the raw bytes of the packet (without type or length prefix) */
|
|
9
|
+
serialize(): Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* UnknownPacket holds a packet whose type is not recognized by this implementation.
|
|
13
|
+
* It round-trips opaquely: the raw bytes are preserved as-is.
|
|
14
|
+
*/
|
|
15
|
+
export declare class UnknownPacket implements ExtensionPacket {
|
|
16
|
+
private readonly packetType;
|
|
17
|
+
private readonly data;
|
|
18
|
+
constructor(packetType: number, data: Uint8Array);
|
|
19
|
+
type(): number;
|
|
20
|
+
serialize(): Uint8Array;
|
|
21
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ import { IndexedDBWalletRepository, IndexedDBContractRepository, InMemoryWalletR
|
|
|
41
41
|
import type { MigrationStatus } from "./repositories";
|
|
42
42
|
import { DelegatorManagerImpl, DelegatorManager } from "./wallet/delegator";
|
|
43
43
|
export * from "./arkfee";
|
|
44
|
-
export * as asset from "./asset";
|
|
44
|
+
export * as asset from "./extension/asset";
|
|
45
45
|
import { ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract } from "./contracts";
|
|
46
46
|
import type { Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams } from "./contracts";
|
|
47
47
|
import { IContractManager } from "./contracts/contractManager";
|
|
@@ -1 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an async iterator over EventSource messages that attaches listeners
|
|
3
|
+
* eagerly (at call time) rather than lazily (at first .next() call).
|
|
4
|
+
* This ensures events are buffered immediately, preventing race conditions
|
|
5
|
+
* where events arrive before iteration begins.
|
|
6
|
+
*/
|
|
1
7
|
export declare function eventSourceIterator(eventSource: EventSource): AsyncGenerator<MessageEvent, void, unknown>;
|
|
@@ -13,30 +13,21 @@ export declare class AssetManager extends ReadonlyAssetManager implements IAsset
|
|
|
13
13
|
* Issue a new asset.
|
|
14
14
|
* @param params - Parameters for asset issuance
|
|
15
15
|
* @param params.amount - Amount of asset units to issue
|
|
16
|
-
* @param params.
|
|
16
|
+
* @param params.controlAssetId - Optional control asset ID (for reissuable assets)
|
|
17
17
|
* @param params.metadata - Optional metadata to attach to the asset
|
|
18
18
|
* @returns Promise resolving to the ark transaction ID and asset ID
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* ```typescript
|
|
22
22
|
* // Issue a simple non-reissuable asset
|
|
23
|
-
* const result = await wallet.
|
|
24
|
-
* console.log('Asset ID:', result.assetId);
|
|
25
|
-
*
|
|
26
|
-
* // Issue a reissuable asset with a new control asset
|
|
27
|
-
* const result = await wallet.issueAsset({
|
|
28
|
-
* amount: 1000,
|
|
29
|
-
* controlAsset: 1 // creates new control asset with amount 1
|
|
30
|
-
* });
|
|
31
|
-
* console.log('Control Asset ID:', result.controlAssetId);
|
|
23
|
+
* const result = await wallet.assetManager.issue({ amount: 1000 });
|
|
32
24
|
* console.log('Asset ID:', result.assetId);
|
|
33
25
|
*
|
|
34
26
|
* // Issue a reissuable asset with an existing control asset
|
|
35
|
-
* const result = await wallet.
|
|
27
|
+
* const result = await wallet.assetManager.issue({
|
|
36
28
|
* amount: 1000,
|
|
37
|
-
*
|
|
29
|
+
* controlAssetId: 'existingControlAssetId'
|
|
38
30
|
* });
|
|
39
|
-
* console.log('Control Asset ID:', result.controlAssetId);
|
|
40
31
|
* console.log('Asset ID:', result.assetId);
|
|
41
32
|
* ```
|
|
42
33
|
*/
|