@subsquid/evm-codec 0.2.1 → 1.0.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.
Files changed (79) hide show
  1. package/lib/codec.d.ts +59 -12
  2. package/lib/codec.d.ts.map +1 -1
  3. package/lib/codec.js.map +1 -1
  4. package/lib/codecs/array.d.ts +3 -3
  5. package/lib/codecs/array.d.ts.map +1 -1
  6. package/lib/codecs/array.js +13 -7
  7. package/lib/codecs/array.js.map +1 -1
  8. package/lib/codecs/primitives.d.ts +2 -16
  9. package/lib/codecs/primitives.d.ts.map +1 -1
  10. package/lib/codecs/primitives.js +52 -141
  11. package/lib/codecs/primitives.js.map +1 -1
  12. package/lib/codecs/struct.d.ts +17 -9
  13. package/lib/codecs/struct.d.ts.map +1 -1
  14. package/lib/codecs/struct.js +53 -39
  15. package/lib/codecs/struct.js.map +1 -1
  16. package/lib/dsl.d.ts +11 -0
  17. package/lib/dsl.d.ts.map +1 -0
  18. package/lib/dsl.js +33 -0
  19. package/lib/dsl.js.map +1 -0
  20. package/lib/index.d.ts +7 -4
  21. package/lib/index.d.ts.map +1 -1
  22. package/lib/index.js +7 -6
  23. package/lib/index.js.map +1 -1
  24. package/lib/sink/bounds.d.ts +21 -0
  25. package/lib/sink/bounds.d.ts.map +1 -0
  26. package/lib/sink/bounds.js +24 -0
  27. package/lib/sink/bounds.js.map +1 -0
  28. package/lib/sink/bytes.d.ts +41 -0
  29. package/lib/sink/bytes.d.ts.map +1 -0
  30. package/lib/sink/bytes.js +261 -0
  31. package/lib/sink/bytes.js.map +1 -0
  32. package/lib/sink/hex.d.ts +33 -0
  33. package/lib/sink/hex.d.ts.map +1 -0
  34. package/lib/sink/hex.js +289 -0
  35. package/lib/sink/hex.js.map +1 -0
  36. package/lib/{src.d.ts → src/bytes.d.ts} +7 -5
  37. package/lib/src/bytes.d.ts.map +1 -0
  38. package/lib/src/bytes.js +161 -0
  39. package/lib/src/bytes.js.map +1 -0
  40. package/lib/src/hex.d.ts +33 -0
  41. package/lib/src/hex.d.ts.map +1 -0
  42. package/lib/src/hex.js +164 -0
  43. package/lib/src/hex.js.map +1 -0
  44. package/lib/util.d.ts +6 -0
  45. package/lib/util.d.ts.map +1 -0
  46. package/lib/util.js +20 -0
  47. package/lib/util.js.map +1 -0
  48. package/package.json +6 -8
  49. package/src/codec.ts +67 -18
  50. package/src/codecs/array.test.ts +87 -0
  51. package/src/codecs/array.ts +9 -12
  52. package/src/codecs/primitives.test.ts +27 -0
  53. package/src/codecs/primitives.ts +87 -191
  54. package/src/codecs/struct.test.ts +69 -0
  55. package/src/codecs/struct.ts +80 -60
  56. package/src/dsl.ts +16 -0
  57. package/src/index.ts +7 -4
  58. package/src/sink/bounds.ts +26 -0
  59. package/src/sink/bytes.test.ts +92 -0
  60. package/src/sink/bytes.ts +290 -0
  61. package/src/sink/hex.ts +311 -0
  62. package/src/src/bytes.test.ts +114 -0
  63. package/src/src/bytes.ts +187 -0
  64. package/src/src/hex.ts +191 -0
  65. package/src/util.ts +19 -0
  66. package/lib/safeToNumber.d.ts +0 -2
  67. package/lib/safeToNumber.d.ts.map +0 -1
  68. package/lib/safeToNumber.js +0 -11
  69. package/lib/safeToNumber.js.map +0 -1
  70. package/lib/sink.d.ts +0 -43
  71. package/lib/sink.d.ts.map +0 -1
  72. package/lib/sink.js +0 -215
  73. package/lib/sink.js.map +0 -1
  74. package/lib/src.d.ts.map +0 -1
  75. package/lib/src.js +0 -141
  76. package/lib/src.js.map +0 -1
  77. package/src/safeToNumber.ts +0 -6
  78. package/src/sink.ts +0 -241
  79. package/src/src.ts +0 -158
package/lib/src/hex.js ADDED
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HexSrc = void 0;
4
+ const codec_1 = require("../codec");
5
+ const HEX_BYTE = 2;
6
+ const WORD_HEX = codec_1.WORD_SIZE * HEX_BYTE;
7
+ const I64_SIGN_BIT = 1n << 63n;
8
+ const I64_RANGE = 1n << 64n;
9
+ const I128_SIGN_BIT = 1n << 127n;
10
+ const I128_RANGE = 1n << 128n;
11
+ const I256_SIGN_BIT = 1n << 255n;
12
+ const I256_RANGE = 1n << 256n;
13
+ const TEXT_DECODER = new TextDecoder('utf-8');
14
+ class HexSrc {
15
+ buf;
16
+ base;
17
+ endChar;
18
+ pos;
19
+ oldPos;
20
+ constructor(hex, strOffset = 2, strEnd = hex.length) {
21
+ this.buf = hex;
22
+ this.base = strOffset;
23
+ this.endChar = strEnd;
24
+ this.pos = strOffset;
25
+ this.oldPos = strOffset;
26
+ }
27
+ slice(start, end) {
28
+ const s = this.base + HEX_BYTE * start;
29
+ const e = end == null ? this.endChar : this.base + HEX_BYTE * end;
30
+ return new HexSrc(this.buf, s, e);
31
+ }
32
+ u8() {
33
+ const start = this.pos + WORD_HEX - 2;
34
+ const val = Number.parseInt(this.buf.slice(start, start + 2), 16);
35
+ this.pos += WORD_HEX;
36
+ return val;
37
+ }
38
+ i8() {
39
+ return Number(this.i256());
40
+ }
41
+ u16() {
42
+ const start = this.pos + WORD_HEX - 4;
43
+ const val = Number.parseInt(this.buf.slice(start, start + 4), 16);
44
+ this.pos += WORD_HEX;
45
+ return val;
46
+ }
47
+ i16() {
48
+ return Number(this.i256());
49
+ }
50
+ u32() {
51
+ const start = this.pos + WORD_HEX - 8;
52
+ const val = Number.parseInt(this.buf.slice(start, start + 8), 16);
53
+ this.pos += WORD_HEX;
54
+ return val;
55
+ }
56
+ i32() {
57
+ return Number(this.i256());
58
+ }
59
+ u64() {
60
+ const start = this.pos + WORD_HEX - 16;
61
+ const val = BigInt(`0x${this.buf.slice(start, start + 16)}`);
62
+ this.pos += WORD_HEX;
63
+ return val;
64
+ }
65
+ i64() {
66
+ const raw = this.u64();
67
+ return raw < I64_SIGN_BIT ? raw : raw - I64_RANGE;
68
+ }
69
+ u128() {
70
+ const start = this.pos + WORD_HEX - 32;
71
+ const val = BigInt(`0x${this.buf.slice(start, start + 32)}`);
72
+ this.pos += WORD_HEX;
73
+ return val;
74
+ }
75
+ i128() {
76
+ const raw = this.u128();
77
+ return raw < I128_SIGN_BIT ? raw : raw - I128_RANGE;
78
+ }
79
+ u256() {
80
+ const val = BigInt(`0x${this.buf.slice(this.pos, this.pos + WORD_HEX)}`);
81
+ this.pos += WORD_HEX;
82
+ return val;
83
+ }
84
+ i256() {
85
+ const raw = this.u256();
86
+ return raw < I256_SIGN_BIT ? raw : raw - I256_RANGE;
87
+ }
88
+ address() {
89
+ const start = this.pos + WORD_HEX - 40;
90
+ const val = `0x${this.buf.slice(start, start + 40)}`;
91
+ this.pos += WORD_HEX;
92
+ return val;
93
+ }
94
+ bytes() {
95
+ const ptr = this.u32();
96
+ this.jump(ptr);
97
+ const len = Number(this.u256());
98
+ this.#assertLength(len, 'bytes');
99
+ const sub = this.buf.slice(this.pos, this.pos + HEX_BYTE * len);
100
+ const val = Buffer.from(sub, 'hex');
101
+ this.jumpBack();
102
+ return val;
103
+ }
104
+ bytesHex() {
105
+ const ptr = this.u32();
106
+ this.jump(ptr);
107
+ const len = Number(this.u256());
108
+ this.#assertLength(len, 'bytes');
109
+ const val = `0x${this.buf.slice(this.pos, this.pos + HEX_BYTE * len)}`;
110
+ this.jumpBack();
111
+ return val;
112
+ }
113
+ staticBytes(len) {
114
+ if (len > 32) {
115
+ throw new Error(`bytes${len} is not a valid type`);
116
+ }
117
+ const sub = this.buf.slice(this.pos, this.pos + HEX_BYTE * len);
118
+ this.pos += WORD_HEX;
119
+ return Buffer.from(sub, 'hex');
120
+ }
121
+ staticBytesHex(len) {
122
+ if (len > 32) {
123
+ throw new Error(`bytes${len} is not a valid type`);
124
+ }
125
+ const val = `0x${this.buf.slice(this.pos, this.pos + HEX_BYTE * len)}`;
126
+ this.pos += WORD_HEX;
127
+ return val;
128
+ }
129
+ string() {
130
+ const ptr = this.u32();
131
+ this.jump(ptr, 'string');
132
+ const len = Number(this.u256());
133
+ this.#assertLength(len, 'string');
134
+ const sub = this.buf.slice(this.pos, this.pos + HEX_BYTE * len);
135
+ const val = TEXT_DECODER.decode(Buffer.from(sub, 'hex'));
136
+ this.jumpBack();
137
+ return val;
138
+ }
139
+ bool() {
140
+ const a = this.buf.charCodeAt(this.pos + WORD_HEX - 2);
141
+ const b = this.buf.charCodeAt(this.pos + WORD_HEX - 1);
142
+ this.pos += WORD_HEX;
143
+ return a !== 48 /* '0' */ || b !== 48;
144
+ }
145
+ #assertLength(len, typeName) {
146
+ if (this.endChar - this.pos < HEX_BYTE * len) {
147
+ throw new RangeError(`Unexpected end of input. Attempting to read ${typeName} of length ${len} from 0x${this.buf.slice(this.base, this.endChar)}`);
148
+ }
149
+ }
150
+ jump(pos, typeName) {
151
+ const target = this.base + HEX_BYTE * pos;
152
+ if (pos < 0 || target >= this.endChar) {
153
+ const what = typeName ? `${typeName} ` : '';
154
+ throw new RangeError(`Unexpected pointer location: 0x${pos.toString(16)}. Attempting to read ${what}from 0x${this.buf.slice(this.base, this.endChar)}`);
155
+ }
156
+ this.oldPos = this.pos;
157
+ this.pos = target;
158
+ }
159
+ jumpBack() {
160
+ this.pos = this.oldPos;
161
+ }
162
+ }
163
+ exports.HexSrc = HexSrc;
164
+ //# sourceMappingURL=hex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hex.js","sourceRoot":"","sources":["../../src/src/hex.ts"],"names":[],"mappings":";;;AAAA,oCAA4C;AAE5C,MAAM,QAAQ,GAAG,CAAC,CAAA;AAClB,MAAM,QAAQ,GAAG,iBAAS,GAAG,QAAQ,CAAA;AAErC,MAAM,YAAY,GAAG,EAAE,IAAI,GAAG,CAAA;AAC9B,MAAM,SAAS,GAAG,EAAE,IAAI,GAAG,CAAA;AAC3B,MAAM,aAAa,GAAG,EAAE,IAAI,IAAI,CAAA;AAChC,MAAM,UAAU,GAAG,EAAE,IAAI,IAAI,CAAA;AAC7B,MAAM,aAAa,GAAG,EAAE,IAAI,IAAI,CAAA;AAChC,MAAM,UAAU,GAAG,EAAE,IAAI,IAAI,CAAA;AAE7B,MAAM,YAAY,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAA;AAE7C,MAAa,MAAM;IACE,GAAG,CAAQ;IACX,IAAI,CAAQ;IACZ,OAAO,CAAQ;IACxB,GAAG,CAAQ;IACX,MAAM,CAAQ;IAEtB,YAAY,GAAW,EAAE,SAAS,GAAG,CAAC,EAAE,SAAiB,GAAG,CAAC,MAAM;QAC/D,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,SAAS,CAAA;QACrB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;QACrB,IAAI,CAAC,GAAG,GAAG,SAAS,CAAA;QACpB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;IAC3B,CAAC;IAED,KAAK,CAAC,KAAa,EAAE,GAAY;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAA;QACtC,MAAM,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,QAAQ,GAAG,GAAG,CAAA;QACjE,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACrC,CAAC;IAED,EAAE;QACE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAA;QACrC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACjE,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,EAAE;QACE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9B,CAAC;IAED,GAAG;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAA;QACrC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACjE,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,GAAG;QACC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9B,CAAC;IAED,GAAG;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAA;QACrC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACjE,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,GAAG;QACC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC9B,CAAC;IAED,GAAG;QACC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,EAAE,CAAA;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,GAAG;QACC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,OAAO,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,SAAS,CAAA;IACrD,CAAC;IAED,IAAI;QACA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,EAAE,CAAA;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,IAAI;QACA,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACvB,OAAO,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,UAAU,CAAA;IACvD,CAAC;IAED,IAAI;QACA,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAA;QACxE,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,IAAI;QACA,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACvB,OAAO,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,UAAU,CAAA;IACvD,CAAC;IAED,OAAO;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,EAAE,CAAA;QACtC,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,EAAE,CAAC,EAAE,CAAA;QACpD,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,KAAK;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACd,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC/B,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAA;QAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,QAAQ,EAAE,CAAA;QACf,OAAO,GAAG,CAAA;IACd,CAAC;IAED,QAAQ;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACd,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC/B,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAChC,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAA;QACtE,IAAI,CAAC,QAAQ,EAAE,CAAA;QACf,OAAO,GAAG,CAAA;IACd,CAAC;IAED,WAAW,CAAC,GAAW;QACnB,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,sBAAsB,CAAC,CAAA;QACtD,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAA;QAC/D,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,cAAc,CAAC,GAAW;QACtB,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,sBAAsB,CAAC,CAAA;QACtD,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAA;QACtE,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,GAAG,CAAA;IACd,CAAC;IAED,MAAM;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QACxB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QAC/B,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAA;QAC/D,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;QACxD,IAAI,CAAC,QAAQ,EAAE,CAAA;QACf,OAAO,GAAG,CAAA;IACd,CAAC;IAED,IAAI;QACA,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAA;QACtD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAA;QACtD,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAA;QACpB,OAAO,CAAC,KAAK,EAAE,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAA;IACzC,CAAC;IAED,aAAa,CAAC,GAAW,EAAE,QAAgB;QACvC,IAAI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,EAAE,CAAC;YAC3C,MAAM,IAAI,UAAU,CAChB,+CAA+C,QAAQ,cAAc,GAAG,WAAW,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAC/H,CAAA;QACL,CAAC;IACL,CAAC;IAED,IAAI,CAAC,GAAW,EAAE,QAAiB;QAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,GAAG,QAAQ,GAAG,GAAG,CAAA;QACzC,IAAI,GAAG,GAAG,CAAC,IAAI,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;YAC3C,MAAM,IAAI,UAAU,CAChB,kCAAkC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,wBAAwB,IAAI,UAAU,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CACpI,CAAA;QACL,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAA;QACtB,IAAI,CAAC,GAAG,GAAG,MAAM,CAAA;IACrB,CAAC;IAED,QAAQ;QACJ,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAA;IAC1B,CAAC;CACJ;AAhLD,wBAgLC"}
package/lib/util.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export type Pretty<T> = {
2
+ [K in keyof T]: T[K];
3
+ } & unknown;
4
+ export declare function propName(prop: string): string;
5
+ export declare function propAccess(prop: string): string;
6
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;KAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAC,GAAG,OAAO,CAAA;AAExD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C"}
package/lib/util.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.propName = propName;
4
+ exports.propAccess = propAccess;
5
+ function propName(prop) {
6
+ if (isValidProperty(prop)) {
7
+ return prop;
8
+ }
9
+ return `[${JSON.stringify(prop)}]`;
10
+ }
11
+ function propAccess(prop) {
12
+ if (isValidProperty(prop)) {
13
+ return `.${prop}`;
14
+ }
15
+ return `[${JSON.stringify(prop)}]`;
16
+ }
17
+ function isValidProperty(s) {
18
+ return /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(s);
19
+ }
20
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAEA,4BAKC;AAED,gCAKC;AAZD,SAAgB,QAAQ,CAAC,IAAY;IACjC,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAA;IACf,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAA;AACtC,CAAC;AAED,SAAgB,UAAU,CAAC,IAAY;IACnC,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,IAAI,EAAE,CAAA;IACrB,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAA;AACtC,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAC9B,OAAO,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAC/C,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@subsquid/evm-codec",
3
- "version": "0.2.1",
3
+ "version": "1.0.0",
4
4
  "description": "EVM encoder/decoder tools",
5
5
  "license": "GPL-3.0-or-later",
6
- "repository": "git@github.com:subsquid/squid.git",
6
+ "repository": "git@github.com:subsquid/squid-sdk.git",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -13,14 +13,12 @@
13
13
  "src"
14
14
  ],
15
15
  "dependencies": {
16
- "@subsquid/util-internal-hex": "^1.2.2"
16
+ "@subsquid/util-internal-hex": "^1.2.3"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/node": "^18.18.14",
20
- "ethers": "^6.9.0",
21
- "typescript": "~5.3.2",
22
- "viem": "^2.8.14",
23
- "vitest": "^1.4.0"
19
+ "@types/node": "^24.0.0",
20
+ "typescript": "5.5.4",
21
+ "vitest": "4.1.5"
24
22
  },
25
23
  "scripts": {
26
24
  "build": "rm -rf lib && tsc -p tsconfig.build.json",
package/src/codec.ts CHANGED
@@ -1,25 +1,74 @@
1
- import type { Sink } from './sink'
2
- import type { Src } from './src'
3
-
4
1
  export const WORD_SIZE = 32
5
2
 
6
- export interface Codec<TIn, TOut = TIn> {
7
- encode(sink: Sink, val: TIn): void
8
- decode(src: Src): TOut
9
- isDynamic: boolean
10
- slotsCount?: number
11
- }
3
+ export type BaseType = 'int' | 'address' | 'bool' | 'bytes' | 'string' | 'array' | 'struct'
12
4
 
13
- export type Struct = {
14
- [key: string]: Codec<any>
5
+ /** ABI encoder backend — writes one 32-byte word per call, manages the head/tail structure. */
6
+ export interface Sink {
7
+ u8(val: number): void
8
+ i8(val: number): void
9
+ u16(val: number): void
10
+ i16(val: number): void
11
+ u32(val: number): void
12
+ i32(val: number): void
13
+ u64(val: bigint): void
14
+ i64(val: bigint): void
15
+ u128(val: bigint): void
16
+ i128(val: bigint): void
17
+ u256(val: bigint): void
18
+ i256(val: bigint): void
19
+ bool(val: boolean): void
20
+ bytes(val: Uint8Array | string): void
21
+ staticBytes(len: number, val: Uint8Array | string): void
22
+ address(val: string): void
23
+ string(val: string): void
24
+ /** Begin the tail of a dynamic type (writes the head offset, enters tail context). */
25
+ openTail(slotsCount?: number): void
26
+ /** Begin the tail of a dynamic array (writes offset + element count, enters tail context). */
27
+ openArray(count: number): void
28
+ /** End the current tail, propagate its size to the parent context. */
29
+ closeTail(): void
30
+ toString(): string
15
31
  }
16
32
 
17
- type Pretty<T> = { [K in keyof T]: T[K] } & unknown
33
+ /** ABI decoder backend reads one 32-byte word per call, follows offset pointers. */
34
+ export interface Src {
35
+ u8(): number
36
+ i8(): number
37
+ u16(): number
38
+ i16(): number
39
+ u32(): number
40
+ i32(): number
41
+ u64(): bigint
42
+ i64(): bigint
43
+ u128(): bigint
44
+ i128(): bigint
45
+ u256(): bigint
46
+ i256(): bigint
47
+ bool(): boolean
48
+ bytes(): Uint8Array
49
+ /** Decode a dynamic `bytes` value as a `0x`-prefixed hex string. */
50
+ bytesHex(): string
51
+ staticBytes(len: number): Uint8Array
52
+ /** Decode a `bytes<N>` value as a `0x`-prefixed hex string. */
53
+ staticBytesHex(len: number): string
54
+ address(): string
55
+ string(): string
56
+ /** Create a sub-view of this source starting at byte offset `start`. */
57
+ slice(start: number, end?: number): Src
58
+ /** Follow an offset pointer: save the current position and jump to `pos`. */
59
+ jump(pos: number, typeName?: string): void
60
+ /** Return to the position saved by the last `jump`. */
61
+ jumpBack(): void
62
+ }
18
63
 
19
- export type DecodedStruct<T extends Struct> = Pretty<{
20
- [K in keyof T]: T[K] extends Codec<any, infer U> ? U : never
21
- }>
64
+ export interface Codec<TIn, TOut = TIn> {
65
+ encode(sink: Sink, val: TIn): void
66
+ decode(src: Src): TOut
67
+ isDynamic: boolean
68
+ slotsCount?: number
69
+ baseType: BaseType
70
+ }
22
71
 
23
- export type EncodedStruct<T extends Struct> = Pretty<{
24
- [K in keyof T]: T[K] extends Codec<infer U, any> ? U : never
25
- }>
72
+ export type Struct = {
73
+ [key: string]: Codec<any>
74
+ }
@@ -0,0 +1,87 @@
1
+ import {describe, expect, it} from 'vitest'
2
+ import {address, array, bytes, fixedSizeArray, int8, BytesSink, BytesSrc, string, uint256} from '..'
3
+
4
+ function roundtrip<T>(codec: {encode: (s: BytesSink, v: T) => void; decode: (s: BytesSrc) => T; slotsCount?: number}, value: T) {
5
+ const sink = new BytesSink(codec.slotsCount ?? 1)
6
+ codec.encode(sink, value)
7
+ expect(codec.decode(new BytesSrc(sink.result()))).toStrictEqual(value)
8
+ }
9
+
10
+ describe('fixed size array', () => {
11
+ it('static types', () => {
12
+ roundtrip(fixedSizeArray(int8, 5), [1, 2, -3, 4, 5])
13
+ roundtrip(fixedSizeArray(int8, 5), [1, 2, -3, -4, 5])
14
+ })
15
+
16
+ it('dynamic types', () => {
17
+ const arr = fixedSizeArray(string, 3)
18
+ roundtrip(arr, [
19
+ 'aaa',
20
+ 'a relatively long string to test what happens when the string is long, longer than 32 bytes or even better, longer than 64 bytes!!!',
21
+ 'dasdas',
22
+ ])
23
+ })
24
+
25
+ it('deep nested arrays', () => {
26
+ const arr = fixedSizeArray(fixedSizeArray(string, 3), 2)
27
+ const data = [
28
+ 'aaa',
29
+ 'a relatively long string to test what happens when the string is long, longer than 32 bytes or even better, longer than 64 bytes!!!',
30
+ 'dasdas',
31
+ ]
32
+ roundtrip(arr, [data, [...data].reverse()])
33
+ })
34
+ })
35
+
36
+ describe('dynamic size array', () => {
37
+ it('static types', () => {
38
+ roundtrip(array(int8), [1, 2, -3, 4, 5])
39
+ roundtrip(array(int8), [1, 2, -3, -4, 5])
40
+ })
41
+
42
+ it('array of arrays', () => {
43
+ roundtrip(array(array(int8)), [
44
+ [1, 2, -3, -4, 5],
45
+ [1, 2, -3, -4, 5],
46
+ ])
47
+ })
48
+
49
+ it('dynamic types', () => {
50
+ roundtrip(array(string), [
51
+ 'aaa',
52
+ 'a relatively long string to test what happens when the string is long, longer than 32 bytes or even better, longer than 64 bytes!!!',
53
+ 'dasdas',
54
+ ])
55
+ })
56
+
57
+ it('hardcore dynamic types', () => {
58
+ const sink = new BytesSink(5)
59
+ const arr1 = array(array(fixedSizeArray(string, 3)))
60
+ const arr2 = array(array(uint256))
61
+ const arr3 = array(fixedSizeArray(bytes, 2))
62
+ const data1 = [
63
+ [
64
+ ['aaa', 'bbb', 'ccc'],
65
+ ['ddd', 'eee', 'fff'],
66
+ ],
67
+ [['ggg', 'hhh', 'iii']],
68
+ ]
69
+ const data2 = [[1n, 2n, 3n], [], [4n]]
70
+ const data3 = [
71
+ ['0x1234', '0x5678'],
72
+ ['0xdead', '0xbeef'],
73
+ ]
74
+ arr1.encode(sink, data1)
75
+ address.encode(sink, '0x1234567890123456789012345678901234567890')
76
+ arr3.encode(sink, data3)
77
+ arr2.encode(sink, data2)
78
+ uint256.encode(sink, 123n)
79
+
80
+ const src = new BytesSrc(sink.result())
81
+ expect(arr1.decode(src)).toStrictEqual(data1)
82
+ expect(address.decode(src)).toBe('0x1234567890123456789012345678901234567890')
83
+ expect(arr3.decode(src)).toStrictEqual(data3)
84
+ expect(arr2.decode(src)).toStrictEqual(data2)
85
+ expect(uint256.decode(src)).toBe(123n)
86
+ })
87
+ })
@@ -1,27 +1,22 @@
1
- import { Codec, WORD_SIZE } from '../codec'
2
- import { Sink } from '../sink'
3
- import { Src } from '../src'
1
+ import { WORD_SIZE, type Codec, type Sink, type Src } from '../codec'
4
2
 
5
3
  export class ArrayCodec<const TIn, const TOut> implements Codec<readonly TIn[], readonly TOut[]> {
6
4
  public readonly isDynamic = true
7
-
5
+ public readonly baseType = 'array'
8
6
  constructor(public readonly item: Codec<TIn, TOut>) {}
9
7
 
10
8
  encode(sink: Sink, val: TIn[]) {
11
- sink.newDynamicDataArea(val.length)
9
+ sink.openArray(val.length)
12
10
  for (let i = 0; i < val.length; i++) {
13
11
  this.item.encode(sink, val[i])
14
12
  }
15
- sink.increaseCurrentDataAreaSize(WORD_SIZE)
16
- sink.endCurrentDataArea()
13
+ sink.closeTail()
17
14
  }
18
15
 
19
16
  decode(src: Src): TOut[] {
20
17
  const offset = src.u32()
21
-
22
- src.safeJump(offset, 'array')
18
+ src.jump(offset)
23
19
  const len = src.u32()
24
-
25
20
  const tmpSrc = src.slice(offset + WORD_SIZE)
26
21
  const val = new Array(len)
27
22
  for (let i = 0; i < val.length; i++) {
@@ -33,8 +28,10 @@ export class ArrayCodec<const TIn, const TOut> implements Codec<readonly TIn[],
33
28
  }
34
29
 
35
30
  export class FixedSizeArrayCodec<const TIn, const TOut> implements Codec<readonly TIn[], readonly TOut[]> {
31
+ public readonly baseType = 'array'
36
32
  public isDynamic: boolean
37
33
  public slotsCount: number
34
+
38
35
  constructor(public readonly item: Codec<TIn, TOut>, public readonly size: number) {
39
36
  this.isDynamic = item.isDynamic && size > 0
40
37
  this.slotsCount = this.isDynamic ? 1 : size
@@ -53,11 +50,11 @@ export class FixedSizeArrayCodec<const TIn, const TOut> implements Codec<readonl
53
50
  }
54
51
 
55
52
  private encodeDynamic(sink: Sink, val: TIn[]) {
56
- sink.newStaticDataArea(this.size)
53
+ sink.openTail(this.size)
57
54
  for (let i = 0; i < val.length; i++) {
58
55
  this.item.encode(sink, val[i])
59
56
  }
60
- sink.endCurrentDataArea()
57
+ sink.closeTail()
61
58
  }
62
59
 
63
60
  decode(src: Src): TOut[] {
@@ -0,0 +1,27 @@
1
+ import {describe, expect, it} from "vitest";
2
+ import {bytes32, BytesSink, BytesSrc, string, uint256} from "..";
3
+
4
+ describe('Strings', () => {
5
+ it('should encode and decode strings', async () => {
6
+ const sink = new BytesSink(1)
7
+ string.encode(sink, 'hello')
8
+ expect(sink.toString()).toEqual('0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000')
9
+ expect(string.decode(new BytesSrc(sink.result()))).toEqual('hello');
10
+ });
11
+
12
+ it('should encode and decode empty string', async () => {
13
+ const sink = new BytesSink(1)
14
+ string.encode(sink, '')
15
+ expect(sink.toString()).toEqual('0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000')
16
+ expect(string.decode(new BytesSrc(sink.result()))).toEqual('');
17
+ });
18
+
19
+ it('should fail on decoding invalid data', async () => {
20
+ let sink = new BytesSink(1)
21
+ uint256.encode(sink, 1337)
22
+ expect(() => string.decode(new BytesSrc(sink.result()))).toThrow('Unexpected pointer location: 0x539. Attempting to read string from 0x0000000000000000000000000000000000000000000000000000000000000539')
23
+ sink = new BytesSink(1)
24
+ bytes32.encode(sink, '0x4b45590000000000000000000000000000000000000000000000000000000000')
25
+ expect(() => string.decode(new BytesSrc(sink.result()))).toThrow('Unexpected end of input. Attempting to read string of length 3.404599034663955e+76 from 0x4b45590000000000000000000000000000000000000000000000000000000000')
26
+ });
27
+ });