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