@subsquid/evm-codec 0.3.0 → 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.
- package/lib/codec.d.ts +57 -12
- package/lib/codec.d.ts.map +1 -1
- package/lib/codec.js.map +1 -1
- package/lib/codecs/array.d.ts +1 -3
- package/lib/codecs/array.d.ts.map +1 -1
- package/lib/codecs/array.js +13 -9
- package/lib/codecs/array.js.map +1 -1
- package/lib/codecs/primitives.d.ts +2 -17
- package/lib/codecs/primitives.d.ts.map +1 -1
- package/lib/codecs/primitives.js +46 -152
- package/lib/codecs/primitives.js.map +1 -1
- package/lib/codecs/struct.d.ts +16 -9
- package/lib/codecs/struct.d.ts.map +1 -1
- package/lib/codecs/struct.js +53 -40
- package/lib/codecs/struct.js.map +1 -1
- package/lib/dsl.d.ts +11 -0
- package/lib/dsl.d.ts.map +1 -0
- package/lib/dsl.js +33 -0
- package/lib/dsl.js.map +1 -0
- package/lib/index.d.ts +7 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +7 -6
- package/lib/index.js.map +1 -1
- package/lib/sink/bounds.d.ts +21 -0
- package/lib/sink/bounds.d.ts.map +1 -0
- package/lib/sink/bounds.js +24 -0
- package/lib/sink/bounds.js.map +1 -0
- package/lib/sink/bytes.d.ts +41 -0
- package/lib/sink/bytes.d.ts.map +1 -0
- package/lib/sink/bytes.js +261 -0
- package/lib/sink/bytes.js.map +1 -0
- package/lib/sink/hex.d.ts +33 -0
- package/lib/sink/hex.d.ts.map +1 -0
- package/lib/sink/hex.js +289 -0
- package/lib/sink/hex.js.map +1 -0
- package/lib/{src.d.ts → src/bytes.d.ts} +7 -5
- package/lib/src/bytes.d.ts.map +1 -0
- package/lib/src/bytes.js +161 -0
- package/lib/src/bytes.js.map +1 -0
- package/lib/src/hex.d.ts +33 -0
- package/lib/src/hex.d.ts.map +1 -0
- package/lib/src/hex.js +164 -0
- package/lib/src/hex.js.map +1 -0
- package/lib/util.d.ts +6 -0
- package/lib/util.d.ts.map +1 -0
- package/lib/util.js +20 -0
- package/lib/util.js.map +1 -0
- package/package.json +6 -8
- package/src/codec.ts +65 -19
- package/src/codecs/array.test.ts +87 -0
- package/src/codecs/array.ts +6 -11
- package/src/codecs/primitives.test.ts +27 -0
- package/src/codecs/primitives.ts +87 -208
- package/src/codecs/struct.test.ts +69 -0
- package/src/codecs/struct.ts +80 -61
- package/src/dsl.ts +16 -0
- package/src/index.ts +7 -4
- package/src/sink/bounds.ts +26 -0
- package/src/sink/bytes.test.ts +92 -0
- package/src/sink/bytes.ts +290 -0
- package/src/sink/hex.ts +311 -0
- package/src/src/bytes.test.ts +114 -0
- package/src/src/bytes.ts +187 -0
- package/src/src/hex.ts +191 -0
- package/src/util.ts +19 -0
- package/lib/safeToNumber.d.ts +0 -2
- package/lib/safeToNumber.d.ts.map +0 -1
- package/lib/safeToNumber.js +0 -11
- package/lib/safeToNumber.js.map +0 -1
- package/lib/sink.d.ts +0 -43
- package/lib/sink.d.ts.map +0 -1
- package/lib/sink.js +0 -215
- package/lib/sink.js.map +0 -1
- package/lib/src.d.ts.map +0 -1
- package/lib/src.js +0 -141
- package/lib/src.js.map +0 -1
- package/src/safeToNumber.ts +0 -6
- package/src/sink.ts +0 -241
- package/src/src.ts +0 -158
package/src/codecs/primitives.ts
CHANGED
|
@@ -1,201 +1,93 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Codec } from '../codec'
|
|
3
|
-
import { Sink } from '../sink'
|
|
4
|
-
import { Src } from '../src'
|
|
5
|
-
import { ArrayCodec, FixedSizeArrayCodec } from './array'
|
|
6
|
-
import { StructCodec } from './struct'
|
|
7
|
-
import { safeToNumber } from '../safeToNumber'
|
|
1
|
+
import type {Codec, Sink, Src} from '../codec'
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
},
|
|
15
|
-
decode(src: Src): boolean {
|
|
16
|
-
return src.bool()
|
|
17
|
-
},
|
|
18
|
-
isDynamic: false,
|
|
19
|
-
baseType: 'bool',
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const uint8: Codec<Numberish, number> = {
|
|
23
|
-
encode(sink: Sink, val: Numberish) {
|
|
24
|
-
sink.u8(safeToNumber(val))
|
|
25
|
-
},
|
|
26
|
-
decode(src: Src): number {
|
|
27
|
-
return src.u8()
|
|
28
|
-
},
|
|
29
|
-
isDynamic: false,
|
|
30
|
-
baseType: 'int',
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const int8: Codec<Numberish, number> = {
|
|
34
|
-
encode(sink: Sink, val: Numberish) {
|
|
35
|
-
sink.i8(safeToNumber(val))
|
|
36
|
-
},
|
|
37
|
-
decode(src: Src): number {
|
|
38
|
-
return src.i8()
|
|
39
|
-
},
|
|
40
|
-
isDynamic: false,
|
|
41
|
-
baseType: 'int',
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const uint16: Codec<Numberish, number> = {
|
|
45
|
-
encode(sink: Sink, val: Numberish) {
|
|
46
|
-
sink.u16(safeToNumber(val))
|
|
47
|
-
},
|
|
48
|
-
decode(src: Src): number {
|
|
49
|
-
return src.u16()
|
|
50
|
-
},
|
|
51
|
-
isDynamic: false,
|
|
52
|
-
baseType: 'int',
|
|
3
|
+
function safeToNumber(value: number | bigint): number {
|
|
4
|
+
if (value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) {
|
|
5
|
+
throw new Error(`${value} is not a safe integer`)
|
|
6
|
+
}
|
|
7
|
+
return Number(value)
|
|
53
8
|
}
|
|
54
9
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
},
|
|
59
|
-
decode(src: Src): number {
|
|
60
|
-
return src.i16()
|
|
61
|
-
},
|
|
62
|
-
isDynamic: false,
|
|
63
|
-
baseType: 'int',
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const uint32: Codec<Numberish, number> = {
|
|
67
|
-
encode(sink: Sink, val: Numberish) {
|
|
68
|
-
sink.u32(safeToNumber(val))
|
|
69
|
-
},
|
|
70
|
-
decode(src: Src): number {
|
|
71
|
-
return src.u32()
|
|
72
|
-
},
|
|
73
|
-
isDynamic: false,
|
|
74
|
-
baseType: 'int',
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export const int32: Codec<Numberish, number> = {
|
|
78
|
-
encode(sink: Sink, val: Numberish) {
|
|
79
|
-
sink.i32(safeToNumber(val))
|
|
80
|
-
},
|
|
81
|
-
decode(src: Src): number {
|
|
82
|
-
return src.i32()
|
|
83
|
-
},
|
|
84
|
-
isDynamic: false,
|
|
85
|
-
baseType: 'int',
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export const uint64: Codec<Numberish, bigint> = {
|
|
89
|
-
encode(sink: Sink, val: Numberish) {
|
|
90
|
-
sink.u64(BigInt(val))
|
|
91
|
-
},
|
|
92
|
-
decode(src: Src): bigint {
|
|
93
|
-
return src.u64()
|
|
94
|
-
},
|
|
95
|
-
isDynamic: false,
|
|
96
|
-
baseType: 'int',
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export const int64: Codec<Numberish, bigint> = {
|
|
100
|
-
encode(sink: Sink, val: Numberish) {
|
|
101
|
-
sink.i64(BigInt(val))
|
|
102
|
-
},
|
|
103
|
-
decode(src: Src): bigint {
|
|
104
|
-
return src.i64()
|
|
105
|
-
},
|
|
106
|
-
isDynamic: false,
|
|
107
|
-
baseType: 'int',
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export const uint128: Codec<Numberish, bigint> = {
|
|
111
|
-
encode(sink: Sink, val: Numberish) {
|
|
112
|
-
sink.u128(BigInt(val))
|
|
113
|
-
},
|
|
114
|
-
decode(src: Src): bigint {
|
|
115
|
-
return src.u128()
|
|
116
|
-
},
|
|
117
|
-
isDynamic: false,
|
|
118
|
-
baseType: 'int',
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export const int128: Codec<Numberish, bigint> = {
|
|
122
|
-
encode(sink: Sink, val: Numberish) {
|
|
123
|
-
sink.i128(BigInt(val))
|
|
124
|
-
},
|
|
125
|
-
decode(src: Src): bigint {
|
|
126
|
-
return src.i128()
|
|
127
|
-
},
|
|
128
|
-
isDynamic: false,
|
|
129
|
-
baseType: 'int',
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export const uint256: Codec<Numberish, bigint> = {
|
|
133
|
-
encode(sink: Sink, val: Numberish) {
|
|
134
|
-
sink.u256(BigInt(val))
|
|
135
|
-
},
|
|
136
|
-
decode(src: Src): bigint {
|
|
137
|
-
return src.u256()
|
|
138
|
-
},
|
|
139
|
-
isDynamic: false,
|
|
140
|
-
baseType: 'int',
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export const int256: Codec<Numberish, bigint> = {
|
|
144
|
-
encode(sink: Sink, val: Numberish) {
|
|
145
|
-
sink.i256(BigInt(val))
|
|
146
|
-
},
|
|
147
|
-
decode(src: Src): bigint {
|
|
148
|
-
return src.i256()
|
|
149
|
-
},
|
|
150
|
-
isDynamic: false,
|
|
151
|
-
baseType: 'int',
|
|
152
|
-
}
|
|
10
|
+
type Numberish = number | bigint
|
|
11
|
+
type NumberType = 'u8' | 'i8' | 'u16' | 'i16' | 'u32' | 'i32'
|
|
12
|
+
type BigIntType = 'u64' | 'i64' | 'u128' | 'i128' | 'u256' | 'i256'
|
|
153
13
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return src.string()
|
|
162
|
-
},
|
|
163
|
-
isDynamic: true,
|
|
164
|
-
baseType: 'string',
|
|
14
|
+
function numberCodec(method: NumberType): Codec<Numberish, number> {
|
|
15
|
+
return {
|
|
16
|
+
encode: (sink, val) => sink[method](safeToNumber(val)),
|
|
17
|
+
decode: (src) => src[method](),
|
|
18
|
+
isDynamic: false,
|
|
19
|
+
baseType: 'int',
|
|
20
|
+
}
|
|
165
21
|
}
|
|
166
22
|
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return decodeHex(val)
|
|
23
|
+
function bigintCodec(method: BigIntType): Codec<Numberish, bigint> {
|
|
24
|
+
return {
|
|
25
|
+
encode: (sink, val) => sink[method](BigInt(val)),
|
|
26
|
+
decode: (src) => src[method](),
|
|
27
|
+
isDynamic: false,
|
|
28
|
+
baseType: 'int',
|
|
29
|
+
}
|
|
175
30
|
}
|
|
176
31
|
|
|
177
|
-
export const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
32
|
+
export const bool: Codec<boolean> = {
|
|
33
|
+
encode(sink: Sink, val: boolean) {
|
|
34
|
+
sink.bool(val)
|
|
35
|
+
},
|
|
36
|
+
decode(src: Src): boolean {
|
|
37
|
+
return src.bool()
|
|
38
|
+
},
|
|
39
|
+
isDynamic: false,
|
|
40
|
+
baseType: 'bool',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const uint8 = numberCodec('u8')
|
|
44
|
+
export const int8 = numberCodec('i8')
|
|
45
|
+
export const uint16 = numberCodec('u16')
|
|
46
|
+
export const int16 = numberCodec('i16')
|
|
47
|
+
export const uint32 = numberCodec('u32')
|
|
48
|
+
export const int32 = numberCodec('i32')
|
|
49
|
+
export const uint64 = bigintCodec('u64')
|
|
50
|
+
export const int64 = bigintCodec('i64')
|
|
51
|
+
export const uint128 = bigintCodec('u128')
|
|
52
|
+
export const int128 = bigintCodec('i128')
|
|
53
|
+
export const uint256 = bigintCodec('u256')
|
|
54
|
+
export const int256 = bigintCodec('i256')
|
|
55
|
+
|
|
56
|
+
export const string: Codec<string> = {
|
|
57
|
+
encode(sink: Sink, val: string) {
|
|
58
|
+
sink.openTail()
|
|
59
|
+
sink.string(val)
|
|
60
|
+
sink.closeTail()
|
|
61
|
+
},
|
|
62
|
+
decode(src: Src): string {
|
|
63
|
+
return src.string()
|
|
64
|
+
},
|
|
65
|
+
isDynamic: true,
|
|
66
|
+
baseType: 'string',
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const bytes: Codec<Uint8Array | string, string> = {
|
|
70
|
+
encode(sink: Sink, val: Uint8Array | string) {
|
|
71
|
+
sink.openTail()
|
|
72
|
+
sink.bytes(val)
|
|
73
|
+
sink.closeTail()
|
|
74
|
+
},
|
|
75
|
+
decode(src: Src): string {
|
|
76
|
+
return src.bytesHex()
|
|
77
|
+
},
|
|
78
|
+
isDynamic: true,
|
|
79
|
+
baseType: 'bytes',
|
|
188
80
|
}
|
|
189
81
|
|
|
190
82
|
const bytesN = (size: number): Codec<Uint8Array | string, string> => ({
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
83
|
+
encode(sink: Sink, val: Uint8Array | string) {
|
|
84
|
+
sink.staticBytes(size, val)
|
|
85
|
+
},
|
|
86
|
+
decode(src: Src): string {
|
|
87
|
+
return src.staticBytesHex(size)
|
|
88
|
+
},
|
|
89
|
+
isDynamic: false,
|
|
90
|
+
baseType: 'bytes',
|
|
199
91
|
})
|
|
200
92
|
|
|
201
93
|
export const bytes0 = bytesN(0)
|
|
@@ -233,28 +125,16 @@ export const bytes31 = bytesN(31)
|
|
|
233
125
|
export const bytes32 = bytesN(32)
|
|
234
126
|
|
|
235
127
|
export const address: Codec<string> = {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export const fixedSizeArray = <TIn, TOut>(item: Codec<TIn, TOut>, size: number): Codec<TIn[], TOut[]> => new FixedSizeArrayCodec(item, size)
|
|
247
|
-
|
|
248
|
-
export const array = <TIn, TOut>(item: Codec<TIn, TOut>): Codec<TIn[], TOut[]> => new ArrayCodec(item)
|
|
249
|
-
|
|
250
|
-
type Struct = {
|
|
251
|
-
[key: string]: Codec<any>
|
|
128
|
+
encode(sink: Sink, val: string) {
|
|
129
|
+
sink.address(val)
|
|
130
|
+
},
|
|
131
|
+
decode(src: Src): string {
|
|
132
|
+
return src.address()
|
|
133
|
+
},
|
|
134
|
+
isDynamic: false,
|
|
135
|
+
baseType: 'address',
|
|
252
136
|
}
|
|
253
137
|
|
|
254
|
-
export const struct = <const T extends Struct>(components: T) => new StructCodec<T>(components)
|
|
255
|
-
|
|
256
|
-
export const tuple = struct
|
|
257
|
-
|
|
258
138
|
export const uint24 = uint32
|
|
259
139
|
export const int24 = int32
|
|
260
140
|
export const uint40 = uint64
|
|
@@ -307,4 +187,3 @@ export const uint240 = uint256
|
|
|
307
187
|
export const int240 = int256
|
|
308
188
|
export const uint248 = uint256
|
|
309
189
|
export const int248 = int256
|
|
310
|
-
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest'
|
|
2
|
+
import {address, array, bytes4, int8, BytesSink, BytesSrc, struct, 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('StructCodec', () => {
|
|
11
|
+
it('static tuple', () => {
|
|
12
|
+
roundtrip(
|
|
13
|
+
struct({
|
|
14
|
+
a: int8,
|
|
15
|
+
b: uint256,
|
|
16
|
+
c: struct({e: address}),
|
|
17
|
+
}),
|
|
18
|
+
{
|
|
19
|
+
a: 1,
|
|
20
|
+
b: 2n,
|
|
21
|
+
c: {e: '0x1234567890123456789012345678901234567890'},
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('dynamic tuple', () => {
|
|
27
|
+
roundtrip(
|
|
28
|
+
struct({
|
|
29
|
+
a: array(uint256),
|
|
30
|
+
b: uint256,
|
|
31
|
+
c: struct({d: array(uint256), e: address}),
|
|
32
|
+
}),
|
|
33
|
+
{
|
|
34
|
+
a: [100n, 1n, 123n],
|
|
35
|
+
b: 2n,
|
|
36
|
+
c: {
|
|
37
|
+
d: [3n, 4n],
|
|
38
|
+
e: '0x1234567890123456789012345678901234567890',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('dynamic tuple with bytes4', () => {
|
|
45
|
+
const s = struct({
|
|
46
|
+
foo: uint256,
|
|
47
|
+
bar: array(uint256),
|
|
48
|
+
str: struct({foo: uint256, bar: bytes4}),
|
|
49
|
+
})
|
|
50
|
+
const value = {
|
|
51
|
+
foo: 100n,
|
|
52
|
+
bar: [1n, 2n, 3n],
|
|
53
|
+
str: {
|
|
54
|
+
foo: 123n,
|
|
55
|
+
bar: '0x12345678',
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
const sink = new BytesSink(1)
|
|
59
|
+
// `bytes4` accepts either `Uint8Array` or the `0x…`-prefixed hex form
|
|
60
|
+
// on the encode side but always returns the hex form on decode — check
|
|
61
|
+
// both input paths explicitly.
|
|
62
|
+
s.encode(sink, {
|
|
63
|
+
...value,
|
|
64
|
+
str: {...value.str, bar: Uint8Array.from([0x12, 0x34, 0x56, 0x78])},
|
|
65
|
+
})
|
|
66
|
+
expect(s.decode(new BytesSrc(sink.result()))).toStrictEqual(value)
|
|
67
|
+
roundtrip(s, value)
|
|
68
|
+
})
|
|
69
|
+
})
|
package/src/codecs/struct.ts
CHANGED
|
@@ -1,75 +1,94 @@
|
|
|
1
|
-
import {Codec,
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import type {Codec, Sink, Src, Struct} from '../codec'
|
|
2
|
+
import {type Pretty, propAccess, propName} from '../util'
|
|
3
|
+
|
|
4
|
+
export type DecodedStruct<T extends Struct> = Pretty<{
|
|
5
|
+
[K in keyof T]: T[K] extends Codec<any, infer U> ? U : never
|
|
6
|
+
}>
|
|
7
|
+
|
|
8
|
+
export type EncodedStruct<T extends Struct> = Pretty<{
|
|
9
|
+
[K in keyof T]: T[K] extends Codec<infer U, any> ? U : never
|
|
10
|
+
}>
|
|
4
11
|
|
|
5
12
|
function slotsCount(codecs: readonly Codec<any>[]) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
let count = 0
|
|
14
|
+
for (const codec of codecs) {
|
|
15
|
+
count += codec.slotsCount ?? 1
|
|
16
|
+
}
|
|
17
|
+
return count
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
export class StructCodec<const T extends Struct> implements Codec<EncodedStruct<T>, DecodedStruct<T>> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
public readonly baseType = 'struct'
|
|
22
|
+
public readonly isDynamic: boolean
|
|
23
|
+
public readonly slotsCount: number
|
|
24
|
+
public readonly childrenSlotsCount: number
|
|
25
|
+
public readonly components: T
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const codecs = Object.values(components)
|
|
23
|
-
this.isDynamic = codecs.some((codec) => codec.isDynamic)
|
|
24
|
-
this.childrenSlotsCount = slotsCount(codecs)
|
|
25
|
-
if (this.isDynamic) {
|
|
26
|
-
this.slotsCount = 1
|
|
27
|
-
} else {
|
|
28
|
-
this.slotsCount = this.childrenSlotsCount
|
|
29
|
-
}
|
|
30
|
-
}
|
|
27
|
+
readonly encodeInline: (sink: Sink, val: EncodedStruct<T>) => void
|
|
28
|
+
readonly decodeInline: (src: Src) => DecodedStruct<T>
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.encodeDynamic(sink, val)
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
for (let i in this.components) {
|
|
38
|
-
let prop = this.components[i]
|
|
39
|
-
prop.encode(sink, val[i])
|
|
40
|
-
}
|
|
41
|
-
}
|
|
30
|
+
readonly encode: (sink: Sink, val: EncodedStruct<T>) => void
|
|
31
|
+
readonly decode: (src: Src) => DecodedStruct<T>
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
33
|
+
constructor(components: T) {
|
|
34
|
+
this.components = components
|
|
35
|
+
const entries = Object.entries(components) as Array<[string, Codec<any>]>
|
|
36
|
+
const codecs = entries.map(([, c]) => c)
|
|
37
|
+
this.isDynamic = codecs.some((codec) => codec.isDynamic)
|
|
38
|
+
this.childrenSlotsCount = slotsCount(codecs)
|
|
39
|
+
this.slotsCount = this.isDynamic ? 1 : this.childrenSlotsCount
|
|
51
40
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
41
|
+
this.encodeInline = this.createEncode(entries, false)
|
|
42
|
+
this.decodeInline = this.createDecode(entries, false)
|
|
43
|
+
this.encode = this.isDynamic ? this.createEncode(entries, true) : this.encodeInline
|
|
44
|
+
this.decode = this.isDynamic ? this.createDecode(entries, true) : this.decodeInline
|
|
55
45
|
}
|
|
56
|
-
let result: any = {}
|
|
57
|
-
for (let i in this.components) {
|
|
58
|
-
let prop = this.components[i]
|
|
59
|
-
result[i] = prop.decode(src)
|
|
60
|
-
}
|
|
61
|
-
return result
|
|
62
|
-
}
|
|
63
46
|
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
// JIT-generates a straight-line encode function. Each child's encode is bound once
|
|
48
|
+
// at construction time to avoid property lookups on the hot path. When wrap=true
|
|
49
|
+
// and the struct is dynamic, openTail/closeTail bracket the field writes so the
|
|
50
|
+
// function can be used as a nested ABI field.
|
|
51
|
+
private createEncode(
|
|
52
|
+
entries: Array<[string, Codec<any>]>,
|
|
53
|
+
wrap: boolean,
|
|
54
|
+
): (sink: Sink, val: EncodedStruct<T>) => void {
|
|
55
|
+
const closureNames: string[] = []
|
|
56
|
+
const closureValues: any[] = []
|
|
57
|
+
let body = ''
|
|
58
|
+
if (wrap && this.isDynamic) {
|
|
59
|
+
body += `sink.openTail(${this.childrenSlotsCount});`
|
|
60
|
+
}
|
|
61
|
+
for (let i = 0; i < entries.length; i++) {
|
|
62
|
+
const [key, child] = entries[i]
|
|
63
|
+
const name = `__e${i}`
|
|
64
|
+
closureNames.push(name)
|
|
65
|
+
closureValues.push(child.encode.bind(child))
|
|
66
|
+
body += `${name}(sink, val${propAccess(key)});`
|
|
67
|
+
}
|
|
68
|
+
if (wrap && this.isDynamic) {
|
|
69
|
+
body += 'sink.closeTail();'
|
|
70
|
+
}
|
|
71
|
+
const fn = new Function(...closureNames, 'sink', 'val', body)
|
|
72
|
+
return fn.bind(null, ...closureValues)
|
|
73
|
+
}
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
private createDecode(entries: Array<[string, Codec<any>]>, wrap: boolean): (src: Src) => DecodedStruct<T> {
|
|
76
|
+
const closureNames: string[] = []
|
|
77
|
+
const closureValues: any[] = []
|
|
78
|
+
let body = ''
|
|
79
|
+
if (wrap && this.isDynamic) {
|
|
80
|
+
body += 'src = src.slice(src.u32());'
|
|
81
|
+
}
|
|
82
|
+
const fields: string[] = []
|
|
83
|
+
for (let i = 0; i < entries.length; i++) {
|
|
84
|
+
const [key, child] = entries[i]
|
|
85
|
+
const name = `__d${i}`
|
|
86
|
+
closureNames.push(name)
|
|
87
|
+
closureValues.push(child.decode.bind(child))
|
|
88
|
+
fields.push(`${propName(key)}:${name}(src)`)
|
|
89
|
+
}
|
|
90
|
+
body += `return {${fields.join(',')}};`
|
|
91
|
+
const fn = new Function(...closureNames, 'src', body)
|
|
92
|
+
return fn.bind(null, ...closureValues)
|
|
72
93
|
}
|
|
73
|
-
return result
|
|
74
|
-
}
|
|
75
94
|
}
|
package/src/dsl.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type {Codec, Struct} from './codec'
|
|
2
|
+
import {ArrayCodec, FixedSizeArrayCodec} from './codecs/array'
|
|
3
|
+
import {StructCodec} from './codecs/struct'
|
|
4
|
+
|
|
5
|
+
export * from './codecs/primitives'
|
|
6
|
+
export {ArrayCodec, FixedSizeArrayCodec} from './codecs/array'
|
|
7
|
+
export {StructCodec, type DecodedStruct, type EncodedStruct} from './codecs/struct'
|
|
8
|
+
|
|
9
|
+
export const fixedSizeArray = <TIn, TOut>(item: Codec<TIn, TOut>, size: number): FixedSizeArrayCodec<TIn, TOut> =>
|
|
10
|
+
new FixedSizeArrayCodec(item, size)
|
|
11
|
+
|
|
12
|
+
export const array = <TIn, TOut>(item: Codec<TIn, TOut>): ArrayCodec<TIn, TOut> => new ArrayCodec(item)
|
|
13
|
+
|
|
14
|
+
export const struct = <const T extends Struct>(components: T): StructCodec<T> => new StructCodec<T>(components)
|
|
15
|
+
|
|
16
|
+
export const tuple = struct
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export * from './
|
|
1
|
+
export * from './codec'
|
|
2
|
+
export * from './dsl'
|
|
3
|
+
export * from './sink/bytes'
|
|
4
|
+
export * from './sink/hex'
|
|
5
|
+
export * from './src/bytes'
|
|
6
|
+
export * from './src/hex'
|
|
7
|
+
export * from './util'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const TEXT_ENCODER = new TextEncoder()
|
|
2
|
+
|
|
3
|
+
export const U256_BASE = 1n << 256n
|
|
4
|
+
|
|
5
|
+
export const U8_MAX = 0xff
|
|
6
|
+
export const U16_MAX = 0xffff
|
|
7
|
+
export const U32_MAX = 0xffffffff
|
|
8
|
+
|
|
9
|
+
export const I8_MIN = -0x80
|
|
10
|
+
export const I8_MAX = 0x7f
|
|
11
|
+
export const I16_MIN = -0x8000
|
|
12
|
+
export const I16_MAX = 0x7fff
|
|
13
|
+
export const I32_MIN = -0x80000000
|
|
14
|
+
export const I32_MAX = 0x7fffffff
|
|
15
|
+
|
|
16
|
+
export const U64_MAX_BI = 0xffffffffffffffffn
|
|
17
|
+
export const I64_MIN_BI = -0x8000000000000000n
|
|
18
|
+
export const I64_MAX_BI = 0x7ffffffffffffffen
|
|
19
|
+
|
|
20
|
+
export const U128_MAX_BI = 0xffffffffffffffffffffffffffffffffn
|
|
21
|
+
export const I128_MIN_BI = -0x80000000000000000000000000000000n
|
|
22
|
+
export const I128_MAX_BI = 0x7fffffffffffffffffffffffffffffffn
|
|
23
|
+
|
|
24
|
+
export const U256_MAX_BI = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn
|
|
25
|
+
export const I256_MIN_BI = -0x8000000000000000000000000000000000000000000000000000000000000000n
|
|
26
|
+
export const I256_MAX_BI = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn
|