@arkade-os/sdk 0.4.0-next.2 → 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 (60) 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/validation.js +6 -2
  13. package/dist/cjs/wallet/wallet.js +5 -4
  14. package/dist/esm/{asset → extension/asset}/index.js +1 -1
  15. package/dist/esm/extension/asset/packet.js +107 -0
  16. package/dist/esm/{asset → extension/asset}/types.js +0 -3
  17. package/dist/esm/extension/index.js +248 -0
  18. package/dist/esm/extension/packet.js +16 -0
  19. package/dist/esm/index.js +1 -1
  20. package/dist/esm/providers/ark.js +52 -37
  21. package/dist/esm/providers/indexer.js +1 -1
  22. package/dist/esm/providers/utils.js +39 -29
  23. package/dist/esm/wallet/asset-manager.js +9 -17
  24. package/dist/esm/wallet/asset.js +1 -1
  25. package/dist/esm/wallet/validation.js +6 -2
  26. package/dist/esm/wallet/wallet.js +5 -4
  27. package/dist/types/{asset → extension/asset}/index.d.ts +1 -1
  28. package/dist/types/extension/asset/packet.d.ts +38 -0
  29. package/dist/types/{asset → extension/asset}/types.d.ts +0 -2
  30. package/dist/types/extension/index.d.ts +56 -0
  31. package/dist/types/extension/packet.d.ts +21 -0
  32. package/dist/types/index.d.ts +1 -1
  33. package/dist/types/providers/utils.d.ts +6 -0
  34. package/dist/types/wallet/asset-manager.d.ts +4 -13
  35. package/dist/types/wallet/asset.d.ts +1 -1
  36. package/package.json +1 -1
  37. package/dist/cjs/asset/packet.js +0 -164
  38. package/dist/esm/asset/packet.js +0 -159
  39. package/dist/types/asset/packet.d.ts +0 -27
  40. /package/dist/cjs/{asset → extension/asset}/assetGroup.js +0 -0
  41. /package/dist/cjs/{asset → extension/asset}/assetId.js +0 -0
  42. /package/dist/cjs/{asset → extension/asset}/assetInput.js +0 -0
  43. /package/dist/cjs/{asset → extension/asset}/assetOutput.js +0 -0
  44. /package/dist/cjs/{asset → extension/asset}/assetRef.js +0 -0
  45. /package/dist/cjs/{asset → extension/asset}/metadata.js +0 -0
  46. /package/dist/cjs/{asset → extension/asset}/utils.js +0 -0
  47. /package/dist/esm/{asset → extension/asset}/assetGroup.js +0 -0
  48. /package/dist/esm/{asset → extension/asset}/assetId.js +0 -0
  49. /package/dist/esm/{asset → extension/asset}/assetInput.js +0 -0
  50. /package/dist/esm/{asset → extension/asset}/assetOutput.js +0 -0
  51. /package/dist/esm/{asset → extension/asset}/assetRef.js +0 -0
  52. /package/dist/esm/{asset → extension/asset}/metadata.js +0 -0
  53. /package/dist/esm/{asset → extension/asset}/utils.js +0 -0
  54. /package/dist/types/{asset → extension/asset}/assetGroup.d.ts +0 -0
  55. /package/dist/types/{asset → extension/asset}/assetId.d.ts +0 -0
  56. /package/dist/types/{asset → extension/asset}/assetInput.d.ts +0 -0
  57. /package/dist/types/{asset → extension/asset}/assetOutput.d.ts +0 -0
  58. /package/dist/types/{asset → extension/asset}/assetRef.d.ts +0 -0
  59. /package/dist/types/{asset → extension/asset}/metadata.d.ts +0 -0
  60. /package/dist/types/{asset → extension/asset}/utils.d.ts +0 -0
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AssetManager = exports.ReadonlyAssetManager = void 0;
4
- const asset_1 = require("../asset");
4
+ const asset_1 = require("../extension/asset");
5
5
  const address_1 = require("../script/address");
6
6
  const asset_2 = require("./asset");
7
+ const extension_1 = require("../extension");
7
8
  const wallet_1 = require("./wallet");
8
9
  class ReadonlyAssetManager {
9
10
  constructor(indexer) {
@@ -24,30 +25,21 @@ class AssetManager extends ReadonlyAssetManager {
24
25
  * Issue a new asset.
25
26
  * @param params - Parameters for asset issuance
26
27
  * @param params.amount - Amount of asset units to issue
27
- * @param params.controlAsset - Optional control asset (for reissuable assets)
28
+ * @param params.controlAssetId - Optional control asset ID (for reissuable assets)
28
29
  * @param params.metadata - Optional metadata to attach to the asset
29
30
  * @returns Promise resolving to the ark transaction ID and asset ID
30
31
  *
31
32
  * @example
32
33
  * ```typescript
33
34
  * // Issue a simple non-reissuable asset
34
- * const result = await wallet.issueAsset({ amount: 1000 });
35
- * console.log('Asset ID:', result.assetId);
36
- *
37
- * // Issue a reissuable asset with a new control asset
38
- * const result = await wallet.issueAsset({
39
- * amount: 1000,
40
- * controlAsset: 1 // creates new control asset with amount 1
41
- * });
42
- * console.log('Control Asset ID:', result.controlAssetId);
35
+ * const result = await wallet.assetManager.issue({ amount: 1000 });
43
36
  * console.log('Asset ID:', result.assetId);
44
37
  *
45
38
  * // Issue a reissuable asset with an existing control asset
46
- * const result = await wallet.issueAsset({
39
+ * const result = await wallet.assetManager.issue({
47
40
  * amount: 1000,
48
- * controlAsset: 'controlAssetId'
41
+ * controlAssetId: 'existingControlAssetId'
49
42
  * });
50
- * console.log('Control Asset ID:', result.controlAssetId);
51
43
  * console.log('Asset ID:', result.assetId);
52
44
  * ```
53
45
  */
@@ -105,7 +97,7 @@ class AssetManager extends ReadonlyAssetManager {
105
97
  script: outputAddress.pkScript,
106
98
  amount: BigInt(totalBtcSelected),
107
99
  },
108
- asset_1.Packet.create(groups).txOut(),
100
+ extension_1.Extension.create([asset_1.Packet.create(groups)]).txOut(),
109
101
  ];
110
102
  const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(coinSelection.inputs, outputs);
111
103
  return {
@@ -217,7 +209,7 @@ class AssetManager extends ReadonlyAssetManager {
217
209
  script: outputAddress.pkScript,
218
210
  amount: BigInt(totalBtcSelected),
219
211
  },
220
- asset_1.Packet.create(groups).txOut(),
212
+ extension_1.Extension.create([asset_1.Packet.create(groups)]).txOut(),
221
213
  ];
222
214
  const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
223
215
  return arkTxid;
@@ -301,7 +293,7 @@ class AssetManager extends ReadonlyAssetManager {
301
293
  script: outputAddress.pkScript,
302
294
  amount: BigInt(totalBtcSelected),
303
295
  },
304
- asset_1.Packet.create(groups).txOut(),
296
+ extension_1.Extension.create([asset_1.Packet.create(groups)]).txOut(),
305
297
  ];
306
298
  const { arkTxid } = await this.wallet.buildAndSubmitOffchainTx(selectedCoins, outputs);
307
299
  return arkTxid;
@@ -4,7 +4,7 @@ exports.createAssetPacket = createAssetPacket;
4
4
  exports.selectCoinsWithAsset = selectCoinsWithAsset;
5
5
  exports.computeAssetChange = computeAssetChange;
6
6
  exports.selectedCoinsToAssetInputs = selectedCoinsToAssetInputs;
7
- const asset_1 = require("../asset");
7
+ const asset_1 = require("../extension/asset");
8
8
  /**
9
9
  * Creates an asset packet from asset inputs and receivers.
10
10
  * Groups inputs and outputs by asset ID and creates the Packet object
@@ -4,7 +4,7 @@ exports.ErrInvalidOffchainOutputAmount = exports.ErrOnchainOutputNotFound = expo
4
4
  exports.validateBatchRecipients = validateBatchRecipients;
5
5
  const utils_js_1 = require("@scure/btc-signer/utils.js");
6
6
  const address_1 = require("../script/address");
7
- const asset_1 = require("../asset");
7
+ const extension_1 = require("../extension");
8
8
  const btc_signer_1 = require("@scure/btc-signer");
9
9
  const ErrOffchainOutputNotFound = (address) => new Error(`offchain send output not found: ${address}`);
10
10
  exports.ErrOffchainOutputNotFound = ErrOffchainOutputNotFound;
@@ -125,7 +125,11 @@ function validateOffchainRecipient(leaves, arkAddress, recipient, usedOutputs //
125
125
  }
126
126
  }
127
127
  function validateAssetOutputs(leafTx, outputIndex, expectedAssets) {
128
- const assetPacket = asset_1.Packet.fromTx(leafTx);
128
+ const ext = extension_1.Extension.fromTx(leafTx);
129
+ const assetPacket = ext.getAssetPacket();
130
+ if (!assetPacket) {
131
+ throw new Error("no asset packet found in extension");
132
+ }
129
133
  for (const { assetId, amount } of expectedAssets) {
130
134
  validateAssetGroupOutput(assetPacket, outputIndex, assetId, amount);
131
135
  }
@@ -30,6 +30,7 @@ const batch_1 = require("./batch");
30
30
  const arkfee_1 = require("../arkfee");
31
31
  const transactionHistory_1 = require("../utils/transactionHistory");
32
32
  const asset_manager_1 = require("./asset-manager");
33
+ const extension_1 = require("../extension");
33
34
  const delegate_1 = require("../script/delegate");
34
35
  const delegator_1 = require("./delegator");
35
36
  const repositories_1 = require("../repositories");
@@ -918,7 +919,7 @@ class Wallet extends ReadonlyWallet {
918
919
  }));
919
920
  if (outputAssets && outputAssets.length > 0) {
920
921
  const assetPacket = (0, asset_1.createAssetPacket)(assetInputs, recipients);
921
- outputs.push(assetPacket.txOut());
922
+ outputs.push(extension_1.Extension.create([assetPacket]).txOut());
922
923
  }
923
924
  // session holds the state of the musig2 signing process of the vtxo tree
924
925
  let session;
@@ -931,15 +932,15 @@ class Wallet extends ReadonlyWallet {
931
932
  this.makeRegisterIntentSignature(params.inputs, outputs, onchainOutputIndexes, signingPublicKeys),
932
933
  this.makeDeleteIntentSignature(params.inputs),
933
934
  ]);
934
- const intentId = await this.safeRegisterIntent(intent);
935
935
  const topics = [
936
936
  ...signingPublicKeys,
937
937
  ...params.inputs.map((input) => `${input.txid}:${input.vout}`),
938
938
  ];
939
- const handler = this.createBatchHandler(intentId, params.inputs, recipients, session);
940
939
  const abortController = new AbortController();
941
940
  try {
942
941
  const stream = this.arkProvider.getEventStream(abortController.signal, topics);
942
+ const intentId = await this.safeRegisterIntent(intent);
943
+ const handler = this.createBatchHandler(intentId, params.inputs, recipients, session);
943
944
  const commitmentTxid = await batch_1.Batch.join(stream, handler, {
944
945
  abortController,
945
946
  skipVtxoTreeSigning: !hasOffchainOutputs,
@@ -1404,7 +1405,7 @@ class Wallet extends ReadonlyWallet {
1404
1405
  recipients.some((r) => r.assets && r.assets.length > 0);
1405
1406
  if (hasAssets) {
1406
1407
  const assetPacket = (0, asset_1.createAssetPacket)(assetInputs, recipients, changeReceiver);
1407
- outputs.push(assetPacket.txOut());
1408
+ outputs.push(extension_1.Extension.create([assetPacket]).txOut());
1408
1409
  }
1409
1410
  const sentAmount = recipients.reduce((sum, r) => sum + r.amount, 0);
1410
1411
  const { arkTxid, signedCheckpointTxs } = await this.buildAndSubmitOffchainTx(selectedCoins, outputs);
@@ -5,4 +5,4 @@ export { AssetInput, AssetInputs } from './assetInput.js';
5
5
  export { AssetOutput, AssetOutputs } from './assetOutput.js';
6
6
  export { Metadata, MetadataList } from './metadata.js';
7
7
  export { AssetGroup } from './assetGroup.js';
8
- export { Packet, AssetPacketNotFoundError } from './packet.js';
8
+ export { Packet } from './packet.js';
@@ -0,0 +1,107 @@
1
+ import { hex } from "@scure/base";
2
+ import { AssetRefType } from './types.js';
3
+ import { AssetGroup } from './assetGroup.js';
4
+ import { BufferReader, BufferWriter } from './utils.js';
5
+ /**
6
+ * Packet represents a collection of asset groups.
7
+ * It encodes/decodes as raw bytes only — OP_RETURN framing is handled by the Extension module.
8
+ */
9
+ export class Packet {
10
+ constructor(groups) {
11
+ this.groups = groups;
12
+ }
13
+ static create(groups) {
14
+ const p = new Packet(groups);
15
+ p.validate();
16
+ return p;
17
+ }
18
+ /**
19
+ * fromBytes parses a Packet from raw bytes.
20
+ */
21
+ static fromBytes(buf) {
22
+ return Packet.fromReader(new BufferReader(buf));
23
+ }
24
+ /**
25
+ * fromString parses a Packet from a raw hex string (not an OP_RETURN script).
26
+ */
27
+ static fromString(s) {
28
+ if (!s) {
29
+ throw new Error("missing packet data");
30
+ }
31
+ let buf;
32
+ try {
33
+ buf = hex.decode(s);
34
+ }
35
+ catch {
36
+ throw new Error("invalid packet format, must be hex");
37
+ }
38
+ return Packet.fromBytes(buf);
39
+ }
40
+ /**
41
+ * type returns the TLV packet type tag. Implements ExtensionPacket interface.
42
+ */
43
+ type() {
44
+ return Packet.PACKET_TYPE;
45
+ }
46
+ leafTxPacket(intentTxid) {
47
+ const leafGroups = this.groups.map((group) => group.toBatchLeafAssetGroup(intentTxid));
48
+ return new Packet(leafGroups);
49
+ }
50
+ /**
51
+ * serialize encodes the packet as raw bytes (varint group count + group data).
52
+ * Does NOT include OP_RETURN, ARK magic, or TLV type/length — those are
53
+ * added by the Extension module.
54
+ */
55
+ serialize() {
56
+ if (this.groups.length === 0) {
57
+ return new Uint8Array(0);
58
+ }
59
+ const writer = new BufferWriter();
60
+ writer.writeVarUint(this.groups.length);
61
+ for (const group of this.groups) {
62
+ group.serializeTo(writer);
63
+ }
64
+ return writer.toBytes();
65
+ }
66
+ /**
67
+ * toString returns the hex-encoded raw packet bytes.
68
+ */
69
+ toString() {
70
+ return hex.encode(this.serialize());
71
+ }
72
+ validate() {
73
+ if (this.groups.length === 0) {
74
+ throw new Error("missing assets");
75
+ }
76
+ const seenAssetIds = new Set();
77
+ for (const group of this.groups) {
78
+ if (group.assetId !== null) {
79
+ const key = group.assetId.toString();
80
+ if (seenAssetIds.has(key)) {
81
+ throw new Error(`duplicate asset group for asset ${key}`);
82
+ }
83
+ seenAssetIds.add(key);
84
+ }
85
+ if (group.controlAsset !== null &&
86
+ group.controlAsset.ref.type === AssetRefType.ByGroup &&
87
+ group.controlAsset.ref.groupIndex >= this.groups.length) {
88
+ throw new Error(`invalid control asset group index, ${group.controlAsset.ref.groupIndex} out of range [0, ${this.groups.length - 1}]`);
89
+ }
90
+ }
91
+ }
92
+ static fromReader(reader) {
93
+ const count = Number(reader.readVarUint());
94
+ const groups = [];
95
+ for (let i = 0; i < count; i++) {
96
+ groups.push(AssetGroup.fromReader(reader));
97
+ }
98
+ if (reader.remaining() > 0) {
99
+ throw new Error(`invalid packet length, left ${reader.remaining()} unknown bytes to read`);
100
+ }
101
+ const packet = new Packet(groups);
102
+ packet.validate();
103
+ return packet;
104
+ }
105
+ }
106
+ /** PACKET_TYPE is the 1-byte TLV type tag used in the Extension envelope. */
107
+ Packet.PACKET_TYPE = 0;
@@ -17,6 +17,3 @@ export var AssetRefType;
17
17
  export const MASK_ASSET_ID = 0x01;
18
18
  export const MASK_CONTROL_ASSET = 0x02;
19
19
  export const MASK_METADATA = 0x04;
20
- // ARK magic bytes and marker
21
- export const ARKADE_MAGIC = new Uint8Array([0x41, 0x52, 0x4b]); // "ARK"
22
- export const MARKER_ASSET_PAYLOAD = 0x00;
@@ -0,0 +1,248 @@
1
+ import { hex } from "@scure/base";
2
+ import { Script } from "@scure/btc-signer";
3
+ import { equalBytes } from "@scure/btc-signer/utils.js";
4
+ import { Packet } from './asset/packet.js';
5
+ import { BufferReader } from './asset/utils.js';
6
+ import { UnknownPacket } from './packet.js';
7
+ export { UnknownPacket } from './packet.js';
8
+ /**
9
+ * ArkadeMagic is the 3-byte magic prefix ("ARK") that identifies an OP_RETURN
10
+ * output as an ark extension blob.
11
+ */
12
+ export const ARKADE_MAGIC = new Uint8Array([0x41, 0x52, 0x4b]); // "ARK"
13
+ /**
14
+ * ErrExtensionNotFound is thrown when no extension output is found in a transaction.
15
+ */
16
+ export class ExtensionNotFoundError extends Error {
17
+ constructor() {
18
+ super("no extension output found in transaction");
19
+ this.name = "ExtensionNotFoundError";
20
+ }
21
+ }
22
+ /**
23
+ * Extension is a set of typed packets encoded in an OP_RETURN output.
24
+ *
25
+ * Wire format:
26
+ * OP_RETURN | <push> | ARK(3B) | [type(1B) | varint_len | data]...
27
+ */
28
+ export class Extension {
29
+ constructor(packets) {
30
+ this.packets = packets;
31
+ }
32
+ static create(packets) {
33
+ if (packets.length === 0) {
34
+ throw new Error("missing packets");
35
+ }
36
+ const seen = new Set();
37
+ for (const p of packets) {
38
+ if (seen.has(p.type())) {
39
+ throw new Error(`duplicate packet type ${p.type()}`);
40
+ }
41
+ seen.add(p.type());
42
+ }
43
+ return new Extension(packets);
44
+ }
45
+ /**
46
+ * isExtension returns true if the script is an OP_RETURN whose push data
47
+ * begins with the ARK magic bytes.
48
+ */
49
+ static isExtension(script) {
50
+ try {
51
+ const decoded = Script.decode(script);
52
+ if (decoded.length < 2 || decoded[0] !== "RETURN")
53
+ return false;
54
+ const data = decoded[1];
55
+ if (!(data instanceof Uint8Array))
56
+ return false;
57
+ return (data.length >= ARKADE_MAGIC.length &&
58
+ equalBytes(data.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC));
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * fromBytes parses an Extension from a raw OP_RETURN script.
66
+ */
67
+ static fromBytes(script) {
68
+ if (!script || script.length === 0) {
69
+ throw new Error("missing OP_RETURN");
70
+ }
71
+ let decoded;
72
+ try {
73
+ decoded = Script.decode(script);
74
+ }
75
+ catch {
76
+ throw new Error("expected OP_RETURN");
77
+ }
78
+ if (decoded.length === 0 || decoded[0] !== "RETURN") {
79
+ throw new Error("expected OP_RETURN");
80
+ }
81
+ const dataPushes = decoded
82
+ .slice(1)
83
+ .filter((x) => x instanceof Uint8Array);
84
+ if (dataPushes.length === 0) {
85
+ throw new Error("missing magic prefix: EOF");
86
+ }
87
+ // Concatenate all data pushes (handles OP_PUSHDATA1/2/4)
88
+ const payload = new Uint8Array(dataPushes.reduce((acc, d) => acc + d.length, 0));
89
+ let offset = 0;
90
+ for (const d of dataPushes) {
91
+ payload.set(d, offset);
92
+ offset += d.length;
93
+ }
94
+ // Check ARK magic
95
+ if (payload.length < ARKADE_MAGIC.length ||
96
+ !equalBytes(payload.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC)) {
97
+ throw new Error(`expected magic prefix ${hex.encode(ARKADE_MAGIC)}, got ${hex.encode(payload.slice(0, Math.min(payload.length, ARKADE_MAGIC.length)))}`);
98
+ }
99
+ // Parse TLV records
100
+ const reader = new BufferReader(payload.slice(ARKADE_MAGIC.length));
101
+ const packets = [];
102
+ while (reader.remaining() > 0) {
103
+ const packetType = reader.readByte();
104
+ let data;
105
+ try {
106
+ data = reader.readVarSlice();
107
+ }
108
+ catch {
109
+ throw new Error("missing packet data");
110
+ }
111
+ packets.push(parsePacket(packetType, data));
112
+ }
113
+ if (packets.length === 0) {
114
+ throw new Error("missing packets");
115
+ }
116
+ // Reject duplicate packet types
117
+ const seen = new Set();
118
+ for (const p of packets) {
119
+ if (seen.has(p.type())) {
120
+ throw new Error(`duplicate packet type ${p.type()}`);
121
+ }
122
+ seen.add(p.type());
123
+ }
124
+ return new Extension(packets);
125
+ }
126
+ /**
127
+ * fromTx searches the transaction outputs for an extension blob and parses it.
128
+ * Throws ExtensionNotFoundError if none is found.
129
+ */
130
+ static fromTx(tx) {
131
+ for (let i = 0; i < tx.outputsLength; i++) {
132
+ const output = tx.getOutput(i);
133
+ if (!output?.script)
134
+ continue;
135
+ if (Extension.isExtension(output.script)) {
136
+ return Extension.fromBytes(output.script);
137
+ }
138
+ }
139
+ throw new ExtensionNotFoundError();
140
+ }
141
+ /**
142
+ * serialize encodes the extension as an OP_RETURN script.
143
+ *
144
+ * Layout: OP_RETURN | <push> | ARK | [type | varint_len | data]...
145
+ */
146
+ serialize() {
147
+ // Build payload: ARK magic + TLV records
148
+ const parts = [ARKADE_MAGIC];
149
+ for (const p of this.packets) {
150
+ const data = p.serialize();
151
+ // type (1 byte)
152
+ const typeByte = new Uint8Array([p.type()]);
153
+ // varint length prefix + data
154
+ const lengthBuf = encodeVarUint(data.length);
155
+ parts.push(typeByte, lengthBuf, data);
156
+ }
157
+ const totalLen = parts.reduce((acc, p) => acc + p.length, 0);
158
+ const payload = new Uint8Array(totalLen);
159
+ let off = 0;
160
+ for (const p of parts) {
161
+ payload.set(p, off);
162
+ off += p.length;
163
+ }
164
+ return buildOpReturnScript(payload);
165
+ }
166
+ /**
167
+ * txOut returns the extension as a zero-value OP_RETURN transaction output.
168
+ */
169
+ txOut() {
170
+ return {
171
+ script: this.serialize(),
172
+ amount: 0n,
173
+ };
174
+ }
175
+ /**
176
+ * getAssetPacket returns the embedded Packet, or null if not present.
177
+ */
178
+ getAssetPacket() {
179
+ for (const p of this.packets) {
180
+ if (p instanceof Packet) {
181
+ return p;
182
+ }
183
+ }
184
+ return null;
185
+ }
186
+ }
187
+ /**
188
+ * parsePacket dispatches to a known packet type or falls back to UnknownPacket.
189
+ */
190
+ function parsePacket(packetType, data) {
191
+ if (packetType === Packet.PACKET_TYPE) {
192
+ return Packet.fromBytes(data);
193
+ }
194
+ return new UnknownPacket(packetType, data);
195
+ }
196
+ /**
197
+ * encodeVarUint encodes a non-negative integer as a LEB128 unsigned varint.
198
+ */
199
+ function encodeVarUint(value) {
200
+ const bytes = [];
201
+ let remaining = value;
202
+ do {
203
+ let byte = remaining & 0x7f;
204
+ remaining >>>= 7;
205
+ if (remaining > 0)
206
+ byte |= 0x80;
207
+ bytes.push(byte);
208
+ } while (remaining > 0);
209
+ return new Uint8Array(bytes);
210
+ }
211
+ /**
212
+ * buildOpReturnScript builds an OP_RETURN script with an arbitrary-length data push.
213
+ * Manually constructed to avoid the 520-byte cap in some script builders.
214
+ *
215
+ * Opcodes: 0x6a=OP_RETURN, 0x4c=OP_PUSHDATA1, 0x4d=OP_PUSHDATA2, 0x4e=OP_PUSHDATA4
216
+ */
217
+ function buildOpReturnScript(data) {
218
+ const n = data.length;
219
+ let script;
220
+ if (n <= 75) {
221
+ script = new Uint8Array(2 + n);
222
+ script[0] = 0x6a; // OP_RETURN
223
+ script[1] = n;
224
+ script.set(data, 2);
225
+ }
226
+ else if (n <= 255) {
227
+ script = new Uint8Array(3 + n);
228
+ script[0] = 0x6a;
229
+ script[1] = 0x4c; // OP_PUSHDATA1
230
+ script[2] = n;
231
+ script.set(data, 3);
232
+ }
233
+ else if (n <= 65535) {
234
+ script = new Uint8Array(4 + n);
235
+ script[0] = 0x6a;
236
+ script[1] = 0x4d; // OP_PUSHDATA2
237
+ new DataView(script.buffer).setUint16(2, n, true);
238
+ script.set(data, 4);
239
+ }
240
+ else {
241
+ script = new Uint8Array(6 + n);
242
+ script[0] = 0x6a;
243
+ script[1] = 0x4e; // OP_PUSHDATA4
244
+ new DataView(script.buffer).setUint32(2, n, true);
245
+ script.set(data, 6);
246
+ }
247
+ return script;
248
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * UnknownPacket holds a packet whose type is not recognized by this implementation.
3
+ * It round-trips opaquely: the raw bytes are preserved as-is.
4
+ */
5
+ export class UnknownPacket {
6
+ constructor(packetType, data) {
7
+ this.packetType = packetType;
8
+ this.data = data;
9
+ }
10
+ type() {
11
+ return this.packetType;
12
+ }
13
+ serialize() {
14
+ return this.data;
15
+ }
16
+ }
package/dist/esm/index.js CHANGED
@@ -35,7 +35,7 @@ import { buildForfeitTx } from './forfeit.js';
35
35
  import { IndexedDBWalletRepository, IndexedDBContractRepository, InMemoryWalletRepository, InMemoryContractRepository, MIGRATION_KEY, migrateWalletRepository, requiresMigration, getMigrationStatus, rollbackMigration, WalletRepositoryImpl, ContractRepositoryImpl, } from './repositories/index.js';
36
36
  import { DelegatorManagerImpl } from './wallet/delegator.js';
37
37
  export * from './arkfee/index.js';
38
- export * as asset from './asset/index.js';
38
+ export * as asset from './extension/asset/index.js';
39
39
  // Contracts
40
40
  import { ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, } from './contracts/index.js';
41
41
  import { closeDatabase, openDatabase } from './repositories/indexedDB/manager.js';
@@ -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";