@fleet-sdk/serializer 0.8.3 → 0.9.0

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/index.js CHANGED
@@ -4,20 +4,20 @@ var common = require('@fleet-sdk/common');
4
4
  var crypto = require('@fleet-sdk/crypto');
5
5
 
6
6
  // src/coders/sigmaByteReader.ts
7
- function hexToBigInt(hex7) {
8
- const value = BigInt(hex7.length % 2 ? `0x0${hex7}` : `0x${hex7}`);
9
- const highByte = Number.parseInt(hex7.slice(0, 2), 16);
7
+ function hexToBigInt(hex8) {
8
+ const value = BigInt(hex8.length % 2 ? `0x0${hex8}` : `0x${hex8}`);
9
+ const highByte = Number.parseInt(hex8.slice(0, 2), 16);
10
10
  if (128 & highByte) return -negateAndMask(value);
11
11
  return value;
12
12
  }
13
13
  function bigIntToHex(value) {
14
14
  const positive = value >= common._0n;
15
- let hex7 = (positive ? value : negateAndMask(value)).toString(16);
16
- if (hex7.length % 2) hex7 = `0${hex7}`;
17
- if (positive && 128 & Number.parseInt(hex7.slice(0, 2), 16)) {
18
- return `00${hex7}`;
15
+ let hex8 = (positive ? value : negateAndMask(value)).toString(16);
16
+ if (hex8.length % 2) hex8 = `0${hex8}`;
17
+ if (positive && 128 & Number.parseInt(hex8.slice(0, 2), 16)) {
18
+ return `00${hex8}`;
19
19
  }
20
- return hex7;
20
+ return hex8;
21
21
  }
22
22
  function negateAndMask(value) {
23
23
  let val = value;
@@ -34,6 +34,22 @@ function negateAndMask(value) {
34
34
  const mask = (1n << BigInt(len)) - 1n;
35
35
  return (~val & mask) + 1n;
36
36
  }
37
+
38
+ // src/coders/numRanges.ts
39
+ var MAX_U8 = 255;
40
+ var MAX_I8 = 127;
41
+ var MIN_I16 = -32768;
42
+ var MAX_I16 = 32767;
43
+ var MIN_I32 = -2147483648;
44
+ var MAX_I32 = 2147483647;
45
+ var MIN_I64 = -BigInt("0x8000000000000000");
46
+ var MAX_I64 = BigInt("0x7fffffffffffffff");
47
+ var MIN_I256 = -BigInt(
48
+ "0x8000000000000000000000000000000000000000000000000000000000000000"
49
+ );
50
+ var MAX_I256 = BigInt(
51
+ "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
52
+ );
37
53
  function writeVLQ(writer, value) {
38
54
  if (value === 0) return writer.write(0);
39
55
  if (value < 0) {
@@ -80,7 +96,7 @@ function readBigVLQ(reader) {
80
96
  if (reader.isEmpty) return common._0n;
81
97
  let value = common._0n;
82
98
  let shift = common._0n;
83
- let lower7bits = common._0n;
99
+ let lower7bits;
84
100
  do {
85
101
  lower7bits = BigInt(reader.readByte());
86
102
  value |= (lower7bits & common._127n) << shift;
@@ -129,33 +145,31 @@ var zigZag64 = {
129
145
  }
130
146
  };
131
147
 
132
- // src/coders/numRanges.ts
133
- var MAX_U8 = 255;
134
- var MAX_I8 = 127;
135
- var MIN_I16 = -32768;
136
- var MAX_I16 = 32767;
137
- var MIN_I32 = -2147483648;
138
- var MAX_I32 = 2147483647;
139
- var MIN_I64 = -BigInt("0x8000000000000000");
140
- var MAX_I64 = BigInt("0x7fffffffffffffff");
141
- var MIN_I256 = -BigInt(
142
- "0x8000000000000000000000000000000000000000000000000000000000000000"
143
- );
144
- var MAX_I256 = BigInt(
145
- "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
146
- );
147
-
148
148
  // src/coders/sigmaByteReader.ts
149
149
  var SigmaByteReader = class {
150
150
  #bytes;
151
151
  #cursor;
152
152
  get isEmpty() {
153
- return common.isEmpty(this.#bytes);
153
+ return this.#bytes.length === this.#cursor;
154
+ }
155
+ get bytes() {
156
+ return this.#bytes;
157
+ }
158
+ get cursor() {
159
+ return this.#cursor;
154
160
  }
155
161
  constructor(bytes) {
156
162
  this.#bytes = crypto.ensureBytes(bytes);
157
163
  this.#cursor = 0;
158
164
  }
165
+ readArray(readFn) {
166
+ const length = this.readUInt();
167
+ const items = new Array(length);
168
+ for (let i = 0; i < length; i++) {
169
+ items[i] = readFn(this, i);
170
+ }
171
+ return items;
172
+ }
159
173
  readBool() {
160
174
  return this.readByte() === 1;
161
175
  }
@@ -179,9 +193,12 @@ var SigmaByteReader = class {
179
193
  readBytes(length) {
180
194
  return this.#bytes.subarray(this.#cursor, this.#cursor += length);
181
195
  }
182
- readVlq() {
196
+ readUInt() {
183
197
  return readVLQ(this);
184
198
  }
199
+ readBigUInt() {
200
+ return readBigVLQ(this);
201
+ }
185
202
  readI8() {
186
203
  const byte = this.readByte();
187
204
  return byte > MAX_I8 ? byte - (MAX_U8 + 1) : byte;
@@ -199,6 +216,22 @@ var SigmaByteReader = class {
199
216
  const len = readVLQ(this);
200
217
  return hexToBigInt(crypto.hex.encode(this.readBytes(len)));
201
218
  }
219
+ readRemainingBytes() {
220
+ return this.readBytes(this.#bytes.length - this.#cursor);
221
+ }
222
+ /**
223
+ * Returns bytes without advancing the cursor.
224
+ */
225
+ peek(count, offset = 0) {
226
+ const begin = this.#cursor + offset;
227
+ return this.#bytes.subarray(begin, begin + count);
228
+ }
229
+ /**
230
+ * Checks if the current position in the byte array starts with the given bytes.
231
+ */
232
+ match(bytes, offset = 0) {
233
+ return common.startsWith(this.#bytes, bytes, this.#cursor + offset);
234
+ }
202
235
  };
203
236
  var SigmaByteWriter = class {
204
237
  #bytes;
@@ -214,37 +247,37 @@ var SigmaByteWriter = class {
214
247
  this.write(value === true ? 1 : 0);
215
248
  return this;
216
249
  }
217
- writeVLQ(value) {
250
+ writeUInt(value) {
218
251
  return writeVLQ(this, value);
219
252
  }
220
- writeBigVLQ(value) {
253
+ writeBigUInt(value) {
221
254
  return writeBigVLQ(this, value);
222
255
  }
223
256
  writeI16(value) {
224
257
  if (value < MIN_I16 || value > MAX_I16) {
225
258
  throw new RangeError(`Value ${value} is out of range for a 16-bit integer`);
226
259
  }
227
- this.writeBigVLQ(zigZag32.encode(value));
260
+ this.writeBigUInt(zigZag32.encode(value));
228
261
  return this;
229
262
  }
230
263
  writeI32(value) {
231
264
  if (value < MIN_I32 || value > MAX_I32) {
232
265
  throw new RangeError(`Value ${value} is out of range for a 32-bit integer`);
233
266
  }
234
- return this.writeBigVLQ(zigZag32.encode(value));
267
+ return this.writeBigUInt(zigZag32.encode(value));
235
268
  }
236
269
  writeI64(value) {
237
270
  if (value < MIN_I64 || value > MAX_I64) {
238
271
  throw new RangeError(`Value ${value} is out of range for a 64-bit integer`);
239
272
  }
240
- return this.writeBigVLQ(zigZag64.encode(value));
273
+ return this.writeBigUInt(zigZag64.encode(value));
241
274
  }
242
275
  writeI256(value) {
243
276
  if (value < MIN_I256 || value > MAX_I256) {
244
277
  throw new RangeError(`Value ${value} is out of range for a 256-bit integer`);
245
278
  }
246
- const hex7 = bigIntToHex(value);
247
- return this.writeVLQ(hex7.length / 2).writeHex(hex7);
279
+ const hex8 = bigIntToHex(value);
280
+ return this.writeUInt(hex8.length / 2).writeHex(hex8);
248
281
  }
249
282
  write(byte) {
250
283
  this.#bytes[this.#cursor++] = byte;
@@ -278,6 +311,23 @@ var SigmaByteWriter = class {
278
311
  const hash = hashFn(this.toBytes());
279
312
  return this.writeBytes(length ? hash.subarray(0, length) : hash);
280
313
  }
314
+ /**
315
+ * Writes a length-delimited array of items to the byte stream using a provided
316
+ * serializer function.
317
+ *
318
+ * @typeParam T - The type of items in the array.
319
+ * @param items - The array of items to serialize and write.
320
+ * @param serializer - A function that serializes each item and writes it using the provided SigmaByteWriter.
321
+ * @returns The current instance of SigmaByteWriter for method chaining.
322
+ */
323
+ writeArray(items, serializer) {
324
+ this.writeUInt(items.length);
325
+ if (items.length === 0) return this;
326
+ for (const item of items) {
327
+ serializer(this, item);
328
+ }
329
+ return this;
330
+ }
281
331
  encode(coder) {
282
332
  return coder.encode(this.toBytes());
283
333
  }
@@ -399,6 +449,14 @@ var SUnitType = class extends SMonomorphicType {
399
449
  return "SUnit";
400
450
  }
401
451
  };
452
+ var SBoxType = class extends SMonomorphicType {
453
+ get code() {
454
+ return 99;
455
+ }
456
+ toString() {
457
+ return "SBox";
458
+ }
459
+ };
402
460
 
403
461
  // src/types/descriptors.ts
404
462
  var constructorCode = Object.freeze({
@@ -441,6 +499,7 @@ var descriptors = {
441
499
  groupElement: new SGroupElementType(),
442
500
  sigmaProp: new SSigmaPropType(),
443
501
  unit: new SUnitType(),
502
+ box: new SBoxType(),
444
503
  coll: collDescriptor,
445
504
  tuple: tupleDescriptor
446
505
  };
@@ -521,14 +580,8 @@ var SByte = monoProxy(SByteType, descriptors.byte);
521
580
  var SBool = monoProxy(SBoolType, descriptors.bool);
522
581
  var SShort = monoProxy(SShortType, descriptors.short);
523
582
  var SInt = monoProxy(SIntType, descriptors.int);
524
- var SLong = monoProxy(
525
- SLongType,
526
- descriptors.long
527
- );
528
- var SBigInt = monoProxy(
529
- SBigIntType,
530
- descriptors.bigInt
531
- );
583
+ var SLong = monoProxy(SLongType, descriptors.long);
584
+ var SBigInt = monoProxy(SBigIntType, descriptors.bigInt);
532
585
  var SGroupElement = monoProxy(
533
586
  SGroupElementType,
534
587
  descriptors.groupElement
@@ -538,6 +591,7 @@ var SSigmaProp = monoProxy(
538
591
  descriptors.sigmaProp
539
592
  );
540
593
  var SUnit = monoProxy(SUnitType, void 0, true);
594
+ var SBox = monoProxy(SBoxType, void 0, true);
541
595
  var SColl = genericProxy(SCollType, (target, _, args) => {
542
596
  const [type, elements] = args;
543
597
  const elementsType = type();
@@ -554,6 +608,143 @@ var SPair = genericProxy(STupleType, (target, _, args) => {
554
608
  }
555
609
  throw new Error("Invalid tuple declaration.");
556
610
  });
611
+ var MAX_UINT16_VALUE = 65535;
612
+ var FEE_CONTRACT_BYTES = crypto.hex.decode(common.FEE_CONTRACT);
613
+ var P2PK_CONTRACT_PREFIX = crypto.hex.decode("0008cd");
614
+ var COMPRESSED_PK_LENGTH = 33;
615
+ var P2PK_CONTRACT_LENGTH = P2PK_CONTRACT_PREFIX.length + COMPRESSED_PK_LENGTH;
616
+ function serializeBox(box, writer = new SigmaByteWriter(4096), distinctTokenIds) {
617
+ writer.writeBigUInt(common.ensureBigInt(box.value)).writeHex(box.ergoTree).writeUInt(box.creationHeight);
618
+ writeTokens(writer, box.assets, distinctTokenIds);
619
+ writeRegisters(writer, box.additionalRegisters);
620
+ if (common.isDefined(distinctTokenIds)) return writer;
621
+ if (!isBox(box)) throw new Error("Invalid box type.");
622
+ return writer.writeHex(box.transactionId).writeUInt(box.index);
623
+ }
624
+ function isBox(box) {
625
+ const castedBox = box;
626
+ return common.isDefined(castedBox.transactionId) && common.isDefined(castedBox.index);
627
+ }
628
+ function writeTokens(writer, tokens, tokenIds) {
629
+ if (tokenIds) {
630
+ writer.writeArray(
631
+ tokens,
632
+ (w, token) => w.writeUInt(tokenIds.indexOf(token.tokenId)).writeBigUInt(common.ensureBigInt(token.amount))
633
+ );
634
+ } else {
635
+ writer.writeArray(
636
+ tokens,
637
+ (w, token) => w.writeHex(token.tokenId).writeBigUInt(common.ensureBigInt(token.amount))
638
+ );
639
+ }
640
+ }
641
+ function writeRegisters(writer, registers) {
642
+ const keys = Object.keys(registers).sort();
643
+ const values = [];
644
+ for (const key of keys) {
645
+ const value = registers[key];
646
+ if (!value) continue;
647
+ values.push(value);
648
+ }
649
+ writer.writeArray(values, (w, value) => w.writeHex(value));
650
+ }
651
+ function estimateBoxSize(box, withValue) {
652
+ if (common.isUndefined(box.creationHeight)) {
653
+ throw new Error("Box size estimation error: creation height is undefined.");
654
+ }
655
+ let size = 0;
656
+ size += estimateVLQSize(common.isDefined(withValue) ? withValue : box.value);
657
+ size += common.byteSizeOf(box.ergoTree);
658
+ size += estimateVLQSize(box.creationHeight);
659
+ size += estimateVLQSize(box.assets.length);
660
+ for (const asset of box.assets) {
661
+ size += common.byteSizeOf(asset.tokenId) + estimateVLQSize(asset.amount);
662
+ }
663
+ let registersLength = 0;
664
+ for (const key in box.additionalRegisters) {
665
+ const register = box.additionalRegisters[key];
666
+ if (register) {
667
+ size += common.byteSizeOf(register);
668
+ registersLength++;
669
+ }
670
+ }
671
+ size += estimateVLQSize(registersLength);
672
+ size += 32;
673
+ size += estimateVLQSize(isBox(box) ? box.index : MAX_UINT16_VALUE);
674
+ return size;
675
+ }
676
+ function deserializeEmbeddedBox(reader, distinctTokenIds, transactionId, index) {
677
+ let begin = reader.cursor;
678
+ const value = reader.readBigUInt();
679
+ const ergoTree = crypto.hex.encode(readErgoTree(reader));
680
+ const creationHeight = reader.readUInt();
681
+ const boxIdWriter = new SigmaByteWriter(4096).writeBytes(reader.bytes.subarray(begin, reader.cursor));
682
+ const assets = readTokens(reader, distinctTokenIds);
683
+ boxIdWriter.writeUInt(assets.length);
684
+ for (const asset of assets) {
685
+ boxIdWriter.writeHex(asset.tokenId).writeBigUInt(asset.amount);
686
+ }
687
+ begin = reader.cursor;
688
+ const additionalRegisters = readRegisters(reader);
689
+ boxIdWriter.writeBytes(reader.bytes.subarray(begin, reader.cursor)).writeHex(transactionId).writeUInt(index);
690
+ return {
691
+ boxId: crypto.hex.encode(crypto.blake2b256(boxIdWriter.toBytes())),
692
+ value,
693
+ ergoTree,
694
+ creationHeight,
695
+ assets,
696
+ additionalRegisters,
697
+ transactionId,
698
+ index
699
+ };
700
+ }
701
+ function deserializeBox(input) {
702
+ const reader = input instanceof SigmaByteReader ? input : new SigmaByteReader(input);
703
+ const begin = reader.cursor;
704
+ const box = {
705
+ boxId: "",
706
+ // placeholder, will be calculated later
707
+ value: reader.readBigUInt(),
708
+ ergoTree: crypto.hex.encode(readErgoTree(reader)),
709
+ creationHeight: reader.readUInt(),
710
+ assets: readTokens(reader),
711
+ additionalRegisters: readRegisters(reader),
712
+ transactionId: crypto.hex.encode(reader.readBytes(32)),
713
+ index: reader.readUInt()
714
+ };
715
+ box.boxId = crypto.hex.encode(crypto.blake2b256(reader.bytes.subarray(begin, reader.cursor)));
716
+ return box;
717
+ }
718
+ function readErgoTree(reader) {
719
+ if (reader.match(FEE_CONTRACT_BYTES)) {
720
+ return reader.readBytes(FEE_CONTRACT_BYTES.length);
721
+ }
722
+ if (reader.match(P2PK_CONTRACT_PREFIX) && crypto.validateEcPoint(reader.peek(COMPRESSED_PK_LENGTH, P2PK_CONTRACT_PREFIX.length))) {
723
+ return reader.readBytes(P2PK_CONTRACT_LENGTH);
724
+ }
725
+ const header = reader.readByte();
726
+ const hasSize = (header & common.ergoTreeHeaderFlags.sizeInclusion) !== 0;
727
+ if (!hasSize) {
728
+ throw new Error("ErgoTree parsing without the size flag is not supported.");
729
+ }
730
+ const size = reader.readUInt();
731
+ return new SigmaByteWriter(1 + 4 + size).write(header).writeUInt(size).writeBytes(reader.readBytes(size)).toBytes();
732
+ }
733
+ function readTokens(reader, tokenIds) {
734
+ return reader.readArray((r) => ({
735
+ tokenId: tokenIds ? tokenIds[r.readUInt()] : crypto.hex.encode(r.readBytes(32)),
736
+ amount: r.readBigUInt()
737
+ }));
738
+ }
739
+ function readRegisters(reader) {
740
+ const registers = {};
741
+ const count = reader.readUInt();
742
+ for (let i = 0; i < count; i++) {
743
+ const value = SConstant.from(reader).toHex();
744
+ registers[`R${(i + 4).toString()}`] = value;
745
+ }
746
+ return registers;
747
+ }
557
748
 
558
749
  // src/serializers/dataSerializer.ts
559
750
  var GROUP_ELEMENT_LENGTH = 33;
@@ -595,7 +786,7 @@ var dataSerializer = {
595
786
  } else {
596
787
  common.assert(Array.isArray(data), `SColl expected an array, got ${typeof data}.`);
597
788
  }
598
- writer.writeVLQ(data.length);
789
+ writer.writeUInt(data.length);
599
790
  switch (type.elementsType.code) {
600
791
  case descriptors.bool.code: {
601
792
  return writer.writeBits(data);
@@ -623,9 +814,8 @@ var dataSerializer = {
623
814
  return writer;
624
815
  }
625
816
  if (type.code === descriptors.unit.code) return writer;
626
- throw Error(
627
- `Serialization error: '0x${type.code.toString(16)}' type not implemented.`
628
- );
817
+ if (type.code === descriptors.box.code) return serializeBox(data, writer);
818
+ throw Error(`Serialization error: '0x${type.code.toString(16)}' type not implemented.`);
629
819
  },
630
820
  deserialize(type, reader) {
631
821
  if (type.embeddable) {
@@ -654,7 +844,7 @@ var dataSerializer = {
654
844
  } else {
655
845
  switch (type.code) {
656
846
  case descriptors.coll.code: {
657
- const length = reader.readVlq();
847
+ const length = reader.readUInt();
658
848
  const embeddedType = type.elementsType;
659
849
  switch (embeddedType.code) {
660
850
  case descriptors.bool.code:
@@ -671,12 +861,12 @@ var dataSerializer = {
671
861
  }
672
862
  }
673
863
  case descriptors.tuple.code: {
674
- return type.elementsType.map(
675
- (t) => this.deserialize(t, reader)
676
- );
864
+ return type.elementsType.map((t) => this.deserialize(t, reader));
677
865
  }
678
866
  case descriptors.unit.code:
679
867
  return void 0;
868
+ case descriptors.box.code:
869
+ return deserializeBox(reader);
680
870
  }
681
871
  }
682
872
  throw new Error(`Parsing error: '0x${type.code.toString(16)}' type not implemented.`);
@@ -688,15 +878,15 @@ var typeSerializer = {
688
878
  writer.write(type.code);
689
879
  } else if (type.code === descriptors.unit.code) {
690
880
  writer.write(type.code);
881
+ } else if (type.code === descriptors.box.code) {
882
+ writer.write(type.code);
691
883
  } else if (isColl(type)) {
692
884
  if (type.elementsType.embeddable) {
693
885
  writer.write(descriptors.coll.simpleCollTypeCode + type.elementsType.code);
694
886
  } else if (isColl(type.elementsType)) {
695
887
  const nestedColl = type.elementsType;
696
888
  if (nestedColl.elementsType.embeddable) {
697
- writer.write(
698
- descriptors.coll.nestedCollTypeCode + nestedColl.elementsType.code
699
- );
889
+ writer.write(descriptors.coll.nestedCollTypeCode + nestedColl.elementsType.code);
700
890
  } else {
701
891
  writer.write(descriptors.coll.simpleCollTypeCode);
702
892
  this.serialize(nestedColl, writer);
@@ -735,12 +925,9 @@ var typeSerializer = {
735
925
  break;
736
926
  default: {
737
927
  const len = type.elementsType.length;
738
- common.assert(
739
- len >= 2 && len <= 255,
740
- "Invalid type: tuples must have between 2 and 255 items."
741
- );
928
+ common.assert(len >= 2 && len <= 255, "Invalid type: tuples must have between 2 and 255 items.");
742
929
  writer.write(descriptors.tuple.genericTupleTypeCode);
743
- writer.writeVLQ(len);
930
+ writer.writeUInt(len);
744
931
  }
745
932
  }
746
933
  for (let i = 0; i < type.elementsType.length; i++) {
@@ -776,28 +963,24 @@ var typeSerializer = {
776
963
  return new STupleType(internal);
777
964
  }
778
965
  case constructorCode.symmetricPair: {
779
- const internal = embdCode === 0 ? [
780
- this.deserialize(r),
781
- this.deserialize(r),
782
- this.deserialize(r),
783
- this.deserialize(r)
784
- ] : [getPrimitiveType(embdCode), getPrimitiveType(embdCode)];
966
+ const internal = embdCode === 0 ? [this.deserialize(r), this.deserialize(r), this.deserialize(r), this.deserialize(r)] : [getPrimitiveType(embdCode), getPrimitiveType(embdCode)];
785
967
  return new STupleType(internal);
786
968
  }
787
969
  }
788
970
  }
789
971
  switch (byte) {
790
972
  case descriptors.tuple.genericTupleTypeCode: {
791
- const len = r.readVlq();
973
+ const len = r.readUInt();
792
974
  const wrapped = new Array(len);
793
975
  for (let i = 0; i < len; i++) {
794
976
  wrapped[i] = this.deserialize(r);
795
977
  }
796
978
  return new STupleType(wrapped);
797
979
  }
798
- case descriptors.unit.code: {
980
+ case descriptors.unit.code:
799
981
  return descriptors.unit;
800
- }
982
+ case descriptors.box.code:
983
+ return descriptors.box;
801
984
  }
802
985
  throw new Error("Not implemented.");
803
986
  }
@@ -808,16 +991,22 @@ var MAX_CONSTANT_LENGTH = 4096;
808
991
  var SConstant = class _SConstant {
809
992
  #type;
810
993
  #data;
994
+ #bytes;
811
995
  constructor(type, data) {
812
996
  this.#type = type;
813
997
  this.#data = type.coerce(data);
814
998
  }
815
999
  static from(bytes) {
816
- common.assert(bytes.length > 0, "Empty constant bytes.");
817
- const reader = new SigmaByteReader(bytes);
1000
+ const reader = bytes instanceof SigmaByteReader ? bytes : new SigmaByteReader(bytes);
1001
+ if (reader.isEmpty) throw new Error("Empty constant bytes.");
1002
+ const start = reader.cursor;
818
1003
  const type = typeSerializer.deserialize(reader);
819
1004
  const data = dataSerializer.deserialize(type, reader);
820
- return new _SConstant(type, data);
1005
+ return new _SConstant(type, data).#withBytes(reader.bytes.slice(start, reader.cursor));
1006
+ }
1007
+ #withBytes(bytes) {
1008
+ this.#bytes = bytes;
1009
+ return this;
821
1010
  }
822
1011
  get type() {
823
1012
  return this.#type;
@@ -825,16 +1014,52 @@ var SConstant = class _SConstant {
825
1014
  get data() {
826
1015
  return this.#data;
827
1016
  }
828
- toBytes() {
829
- const writer = new SigmaByteWriter(MAX_CONSTANT_LENGTH);
1017
+ /**
1018
+ * Returns the serialized representation of the current instance as a `Uint8Array`.
1019
+ * If the bytes have already been computed and cached, returns the cached value.
1020
+ * Otherwise, serializes the instance and returns the resulting bytes.
1021
+ */
1022
+ get bytes() {
1023
+ if (this.#bytes) return this.#bytes;
1024
+ return this.serialize();
1025
+ }
1026
+ /**
1027
+ * Serializes the current object into a `Uint8Array`.
1028
+ */
1029
+ serialize() {
1030
+ const writer = new SigmaByteWriter(guessConstantBytesSize(this.type, this.data));
830
1031
  typeSerializer.serialize(this.type, writer);
831
1032
  dataSerializer.serialize(this.data, this.type, writer);
832
- return writer.toBytes();
1033
+ this.#bytes = writer.toBytes();
1034
+ return this.#bytes;
1035
+ }
1036
+ /**
1037
+ * @deprecated use `serialize` instead
1038
+ */
1039
+ toBytes() {
1040
+ return this.serialize();
833
1041
  }
834
1042
  toHex() {
835
- return crypto.hex.encode(this.toBytes());
1043
+ return crypto.hex.encode(this.serialize());
836
1044
  }
837
1045
  };
1046
+ function guessConstantBytesSize(type, data) {
1047
+ const dataSize = 1;
1048
+ if (type.code === descriptors.short.code) return dataSize + 8;
1049
+ if (type.code === descriptors.int.code) return dataSize + 16;
1050
+ if (type.code === descriptors.long.code) return dataSize + 32;
1051
+ if (type.code === descriptors.bigInt.code) return dataSize + 64;
1052
+ if (type.code === descriptors.bool.code) return dataSize + 1;
1053
+ if (type.code === descriptors.byte.code) return dataSize + 1;
1054
+ if (type.code === descriptors.unit.code) return dataSize + 0;
1055
+ if (type.code === descriptors.groupElement.code) return dataSize + 33;
1056
+ if (type.code === descriptors.sigmaProp.code) return dataSize + 35;
1057
+ if (isColl(type) && !isColl(type.elementsType) && !isTuple(type.elementsType)) {
1058
+ const len = data.length;
1059
+ return dataSize + estimateVLQSize(len) + guessConstantBytesSize(type.elementsType) * len;
1060
+ }
1061
+ return MAX_CONSTANT_LENGTH;
1062
+ }
838
1063
  function decode(value) {
839
1064
  if (value === void 0) return;
840
1065
  try {
@@ -860,97 +1085,26 @@ function parse(constant, mode = "strict") {
860
1085
  return;
861
1086
  }
862
1087
  }
863
- var MAX_UINT16_VALUE = 65535;
864
- function serializeBox(box, writer = new SigmaByteWriter(4096), distinctTokenIds) {
865
- writer.writeBigVLQ(common.ensureBigInt(box.value));
866
- writer.writeHex(box.ergoTree);
867
- writer.writeVLQ(box.creationHeight);
868
- writeTokens(writer, box.assets, distinctTokenIds);
869
- writeRegisters(writer, box.additionalRegisters);
870
- if (common.isDefined(distinctTokenIds)) return writer;
871
- if (!isBox(box)) throw new Error("Invalid box type.");
872
- return writer.writeHex(box.transactionId).writeVLQ(box.index);
873
- }
874
- function isBox(box) {
875
- const castedBox = box;
876
- return common.isDefined(castedBox.transactionId) && common.isDefined(castedBox.index);
877
- }
878
- function writeTokens(writer, tokens, tokenIds) {
879
- if (common.isEmpty(tokens)) {
880
- writer.write(0);
881
- return;
882
- }
883
- writer.writeVLQ(tokens.length);
884
- if (common.some(tokenIds)) {
885
- tokens.map(
886
- (token) => writer.writeVLQ(tokenIds.indexOf(token.tokenId)).writeBigVLQ(common.ensureBigInt(token.amount))
887
- );
888
- } else {
889
- tokens.map(
890
- (token) => writer.writeHex(token.tokenId).writeBigVLQ(common.ensureBigInt(token.amount))
891
- );
892
- }
893
- }
894
- function writeRegisters(writer, registers) {
895
- const keys = Object.keys(registers).sort();
896
- let length = 0;
897
- for (const key of keys) {
898
- if (registers[key]) length++;
899
- }
900
- writer.writeVLQ(length);
901
- if (length === 0) return;
902
- for (const key of keys) {
903
- const register = registers[key];
904
- if (common.isDefined(register)) writer.writeHex(register);
905
- }
906
- }
907
- function estimateBoxSize(box, withValue) {
908
- if (common.isUndefined(box.creationHeight)) {
909
- throw new Error("Box size estimation error: creation height is undefined.");
910
- }
911
- let size = 0;
912
- size += estimateVLQSize(common.isDefined(withValue) ? withValue : box.value);
913
- size += common.byteSizeOf(box.ergoTree);
914
- size += estimateVLQSize(box.creationHeight);
915
- size += estimateVLQSize(box.assets.length);
916
- for (const asset of box.assets) {
917
- size += common.byteSizeOf(asset.tokenId) + estimateVLQSize(asset.amount);
918
- }
919
- let registersLength = 0;
920
- for (const key in box.additionalRegisters) {
921
- const register = box.additionalRegisters[key];
922
- if (register) {
923
- size += common.byteSizeOf(register);
924
- registersLength++;
925
- }
926
- }
927
- size += estimateVLQSize(registersLength);
928
- size += 32;
929
- size += estimateVLQSize(isBox(box) ? box.index : MAX_UINT16_VALUE);
930
- return size;
931
- }
932
1088
  function serializeTransaction(transaction) {
933
- const writer = new SigmaByteWriter(1e5);
934
- writer.writeVLQ(transaction.inputs.length);
935
- transaction.inputs.map((input) => writeInput(writer, input));
936
- writer.writeVLQ(transaction.dataInputs.length);
937
- transaction.dataInputs.map((dataInput) => writer.writeHex(dataInput.boxId));
938
- const distinctTokenIds = getDistinctTokenIds(transaction.outputs);
939
- writer.writeVLQ(distinctTokenIds.length);
940
- distinctTokenIds.map((tokenId) => writer.writeHex(tokenId));
941
- writer.writeVLQ(transaction.outputs.length);
942
- transaction.outputs.map((output) => serializeBox(output, writer, distinctTokenIds));
943
- return writer;
1089
+ const tokenIds = getDistinctTokenIds(transaction.outputs);
1090
+ return new SigmaByteWriter(1e5).writeArray(transaction.inputs, (w, input) => writeInput(w, input)).writeArray(transaction.dataInputs, (w, dataInput) => w.writeHex(dataInput.boxId)).writeArray(tokenIds, (w, tokenId) => w.writeHex(tokenId)).writeArray(transaction.outputs, (w, output) => serializeBox(output, w, tokenIds));
944
1091
  }
945
1092
  function writeInput(writer, input) {
946
- writer.writeHex(input.boxId);
947
1093
  if (isSignedInput(input)) {
948
- writeProof(writer, input.spendingProof?.proofBytes);
949
- writeExtension(writer, input.spendingProof?.extension);
1094
+ writeSignedInput(writer, input);
950
1095
  return;
951
1096
  }
1097
+ writeUnsignedInput(writer, input);
1098
+ }
1099
+ function writeSignedInput(writer, input) {
1100
+ writer.writeHex(input.boxId);
1101
+ writeProof(writer, input.spendingProof?.proofBytes);
1102
+ writeExtension(writer, input.spendingProof?.extension);
1103
+ }
1104
+ function writeUnsignedInput(writer, input) {
1105
+ writer.writeHex(input.boxId);
952
1106
  writeProof(writer, null);
953
- writeExtension(writer, input.extension);
1107
+ writeExtension(writer, isSignedInput(input) ? input.spendingProof?.extension : input.extension);
954
1108
  }
955
1109
  function isSignedInput(input) {
956
1110
  return input.spendingProof !== void 0;
@@ -961,8 +1115,7 @@ function writeProof(writer, proof) {
961
1115
  return;
962
1116
  }
963
1117
  const bytes = crypto.hex.decode(proof);
964
- writer.writeVLQ(bytes.length);
965
- writer.writeBytes(bytes);
1118
+ writer.writeUInt(bytes.length).writeBytes(bytes);
966
1119
  }
967
1120
  function writeExtension(writer, extension) {
968
1121
  if (!extension) {
@@ -970,29 +1123,55 @@ function writeExtension(writer, extension) {
970
1123
  return;
971
1124
  }
972
1125
  const keys = Object.keys(extension);
973
- let length = 0;
1126
+ const values = [];
974
1127
  for (const key of keys) {
975
- if (common.isDefined(extension[key])) length++;
976
- }
977
- writer.writeVLQ(length);
978
- if (length === 0) return;
979
- for (const key of keys) {
980
- const val = extension[key];
981
- if (common.isDefined(val)) {
982
- writer.writeVLQ(Number(key)).writeHex(val);
983
- }
1128
+ const value = extension[key];
1129
+ if (!value) continue;
1130
+ values.push([key, value]);
984
1131
  }
1132
+ writer.writeArray(values, (w, [key, value]) => w.writeUInt(Number(key)).writeHex(value));
985
1133
  }
986
1134
  function getDistinctTokenIds(outputs) {
987
1135
  const tokenIds = /* @__PURE__ */ new Set();
988
1136
  outputs.flatMap((output) => output.assets.map((asset) => tokenIds.add(asset.tokenId)));
989
1137
  return Array.from(tokenIds);
990
1138
  }
1139
+ function deserializeTransaction(input) {
1140
+ const reader = new SigmaByteReader(input);
1141
+ const inputs = reader.readArray(readInput);
1142
+ const id = computeId(reader, inputs);
1143
+ const dataInputs = reader.readArray((r) => ({ boxId: crypto.hex.encode(r.readBytes(32)) }));
1144
+ const tokenIds = reader.readArray((r) => crypto.hex.encode(r.readBytes(32)));
1145
+ const outputs = reader.readArray((r, i) => deserializeEmbeddedBox(r, tokenIds, id, i));
1146
+ return {
1147
+ id,
1148
+ inputs,
1149
+ dataInputs,
1150
+ outputs
1151
+ };
1152
+ }
1153
+ function readInput(reader) {
1154
+ const boxId = crypto.hex.encode(reader.readBytes(32));
1155
+ const proofLength = reader.readUInt();
1156
+ const proofBytes = proofLength > 0 ? crypto.hex.encode(reader.readBytes(proofLength)) : null;
1157
+ const extensionLength = reader.readUInt();
1158
+ const extension = {};
1159
+ for (let i = 0; i < extensionLength; i++) {
1160
+ extension[reader.readUInt()] = SConstant.from(reader).toHex();
1161
+ }
1162
+ return proofBytes ? { boxId, spendingProof: { proofBytes, extension } } : { boxId, extension };
1163
+ }
1164
+ function computeId(reader, inputs) {
1165
+ const bytes = new SigmaByteWriter(reader.bytes.length).writeArray(inputs, (w, input) => writeUnsignedInput(w, input)).writeBytes(reader.bytes.subarray(reader.cursor)).toBytes();
1166
+ return crypto.hex.encode(crypto.blake2b256(bytes));
1167
+ }
991
1168
 
992
1169
  exports.SBigInt = SBigInt;
993
1170
  exports.SBigIntType = SBigIntType;
994
1171
  exports.SBool = SBool;
995
1172
  exports.SBoolType = SBoolType;
1173
+ exports.SBox = SBox;
1174
+ exports.SBoxType = SBoxType;
996
1175
  exports.SByte = SByte;
997
1176
  exports.SByteType = SByteType;
998
1177
  exports.SColl = SColl;
@@ -1020,6 +1199,9 @@ exports.SigmaByteReader = SigmaByteReader;
1020
1199
  exports.SigmaByteWriter = SigmaByteWriter;
1021
1200
  exports.dataSerializer = dataSerializer;
1022
1201
  exports.decode = decode;
1202
+ exports.deserializeBox = deserializeBox;
1203
+ exports.deserializeEmbeddedBox = deserializeEmbeddedBox;
1204
+ exports.deserializeTransaction = deserializeTransaction;
1023
1205
  exports.estimateBoxSize = estimateBoxSize;
1024
1206
  exports.estimateVLQSize = estimateVLQSize;
1025
1207
  exports.isColl = isColl;