@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.
Files changed (64) hide show
  1. package/dist/cjs/{asset → extension/asset}/index.js +1 -2
  2. package/dist/cjs/extension/asset/packet.js +111 -0
  3. package/dist/cjs/{asset → extension/asset}/types.js +1 -4
  4. package/dist/cjs/extension/index.js +254 -0
  5. package/dist/cjs/extension/packet.js +20 -0
  6. package/dist/cjs/index.js +1 -1
  7. package/dist/cjs/providers/ark.js +52 -37
  8. package/dist/cjs/providers/indexer.js +1 -1
  9. package/dist/cjs/providers/utils.js +39 -29
  10. package/dist/cjs/wallet/asset-manager.js +9 -17
  11. package/dist/cjs/wallet/asset.js +1 -1
  12. package/dist/cjs/wallet/serviceWorker/wallet.js +9 -1
  13. package/dist/cjs/wallet/validation.js +6 -2
  14. package/dist/cjs/wallet/wallet.js +5 -4
  15. package/dist/cjs/worker/messageBus.js +22 -2
  16. package/dist/esm/{asset → extension/asset}/index.js +1 -1
  17. package/dist/esm/extension/asset/packet.js +107 -0
  18. package/dist/esm/{asset → extension/asset}/types.js +0 -3
  19. package/dist/esm/extension/index.js +248 -0
  20. package/dist/esm/extension/packet.js +16 -0
  21. package/dist/esm/index.js +1 -1
  22. package/dist/esm/providers/ark.js +52 -37
  23. package/dist/esm/providers/indexer.js +1 -1
  24. package/dist/esm/providers/utils.js +39 -29
  25. package/dist/esm/wallet/asset-manager.js +9 -17
  26. package/dist/esm/wallet/asset.js +1 -1
  27. package/dist/esm/wallet/serviceWorker/wallet.js +9 -1
  28. package/dist/esm/wallet/validation.js +6 -2
  29. package/dist/esm/wallet/wallet.js +5 -4
  30. package/dist/esm/worker/messageBus.js +22 -2
  31. package/dist/types/{asset → extension/asset}/index.d.ts +1 -1
  32. package/dist/types/extension/asset/packet.d.ts +38 -0
  33. package/dist/types/{asset → extension/asset}/types.d.ts +0 -2
  34. package/dist/types/extension/index.d.ts +56 -0
  35. package/dist/types/extension/packet.d.ts +21 -0
  36. package/dist/types/index.d.ts +1 -1
  37. package/dist/types/providers/utils.d.ts +6 -0
  38. package/dist/types/wallet/asset-manager.d.ts +4 -13
  39. package/dist/types/wallet/asset.d.ts +1 -1
  40. package/package.json +1 -1
  41. package/dist/cjs/asset/packet.js +0 -164
  42. package/dist/esm/asset/packet.js +0 -159
  43. package/dist/types/asset/packet.d.ts +0 -27
  44. /package/dist/cjs/{asset → extension/asset}/assetGroup.js +0 -0
  45. /package/dist/cjs/{asset → extension/asset}/assetId.js +0 -0
  46. /package/dist/cjs/{asset → extension/asset}/assetInput.js +0 -0
  47. /package/dist/cjs/{asset → extension/asset}/assetOutput.js +0 -0
  48. /package/dist/cjs/{asset → extension/asset}/assetRef.js +0 -0
  49. /package/dist/cjs/{asset → extension/asset}/metadata.js +0 -0
  50. /package/dist/cjs/{asset → extension/asset}/utils.js +0 -0
  51. /package/dist/esm/{asset → extension/asset}/assetGroup.js +0 -0
  52. /package/dist/esm/{asset → extension/asset}/assetId.js +0 -0
  53. /package/dist/esm/{asset → extension/asset}/assetInput.js +0 -0
  54. /package/dist/esm/{asset → extension/asset}/assetOutput.js +0 -0
  55. /package/dist/esm/{asset → extension/asset}/assetRef.js +0 -0
  56. /package/dist/esm/{asset → extension/asset}/metadata.js +0 -0
  57. /package/dist/esm/{asset → extension/asset}/utils.js +0 -0
  58. /package/dist/types/{asset → extension/asset}/assetGroup.d.ts +0 -0
  59. /package/dist/types/{asset → extension/asset}/assetId.d.ts +0 -0
  60. /package/dist/types/{asset → extension/asset}/assetInput.d.ts +0 -0
  61. /package/dist/types/{asset → extension/asset}/assetOutput.d.ts +0 -0
  62. /package/dist/types/{asset → extension/asset}/assetRef.d.ts +0 -0
  63. /package/dist/types/{asset → extension/asset}/metadata.d.ts +0 -0
  64. /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
- async *getEventStream(signal, topics) {
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
- while (!signal?.aborted) {
230
- try {
231
- const eventSource = new EventSource(url + queryParams);
232
- // Set up abort handling
233
- const abortHandler = () => {
234
- eventSource.close();
235
- };
236
- signal?.addEventListener("abort", abortHandler);
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
- for await (const event of eventSourceIterator(eventSource)) {
239
- if (signal?.aborted)
240
- break;
241
- try {
242
- const data = JSON.parse(event.data);
243
- const settlementEvent = this.parseSettlementEvent(data);
244
- if (settlementEvent) {
245
- yield settlementEvent;
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
- finally {
255
- signal?.removeEventListener("abort", abortHandler);
256
- eventSource.close();
257
- }
258
- }
259
- catch (error) {
260
- if (error instanceof Error && error.name === "AbortError") {
261
- break;
262
- }
263
- // ignore timeout errors, they're expected when the server is not sending anything for 5 min
264
- if (isFetchTimeoutError(error)) {
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
- export async function* eventSourceIterator(eventSource) {
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
- try {
29
- while (true) {
30
- // if we have queued messages, yield the first one, remove it from the queue
31
- if (messageQueue.length > 0) {
32
- yield messageQueue.shift();
33
- continue;
34
- }
35
- // if we have queued errors, throw the first one, remove it from the queue
36
- if (errorQueue.length > 0) {
37
- const error = errorQueue.shift();
38
- throw error;
39
- }
40
- // wait for the next message or error
41
- const result = await new Promise((resolve, reject) => {
42
- messageResolve = resolve;
43
- errorResolve = reject;
44
- }).finally(() => {
45
- messageResolve = null;
46
- errorResolve = null;
47
- });
48
- if (result) {
49
- yield result;
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
- finally {
54
- // clean up
55
- eventSource.removeEventListener("message", messageHandler);
56
- eventSource.removeEventListener("error", errorHandler);
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.controlAsset - Optional control asset (for reissuable assets)
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.issueAsset({ amount: 1000 });
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.issueAsset({
35
+ * const result = await wallet.assetManager.issue({
43
36
  * amount: 1000,
44
- * controlAsset: 'controlAssetId'
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;
@@ -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
- navigator.serviceWorker.removeEventListener("message", messageHandler);
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 { Packet } from '../asset/index.js';
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 assetPacket = Packet.fromTx(leafTx);
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
- return;
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, AssetPacketNotFoundError } from "./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
+ }
@@ -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.controlAsset - Optional control asset (for reissuable assets)
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.issueAsset({ amount: 1000 });
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.issueAsset({
27
+ * const result = await wallet.assetManager.issue({
36
28
  * amount: 1000,
37
- * controlAsset: 'controlAssetId'
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
  */
@@ -1,4 +1,4 @@
1
- import { Packet } from "../asset";
1
+ import { Packet } from "../extension/asset";
2
2
  import { Asset, Recipient, VirtualCoin } from "./index";
3
3
  /**
4
4
  * Creates an asset packet from asset inputs and receivers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.4.0-next.1",
3
+ "version": "0.4.0-next.3",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",