Skip to content

feat: add SerializedListType and ProgressiveSerializedListType - #555

Merged
wemeetagain merged 6 commits into
masterfrom
te/serialized_list
Oct 16, 2026
Merged

wemeetagain merged 6 commits into
masterfrom
te/serialized_list

Conversation

@twoeths

@twoeths twoeths commented Oct 15, 2026

Copy link
Copy Markdown
Member

Motivation

  • The Transactions type is so expensive to deserialize, for example with a 4M containing 1M transactions it could end up > 200MB
  • We write a new type to track Transactions in a single Uint8Array instance, given the fact that when we deserialize from ssz, these serialized transactions stay next to each other

Description

  • new SerializedList class value to track Uint8Array[] as a single Uint8Array and offsets: Uint32Array
  • new SerializedListType for pre-gloas, ProgressiveSerializedListType for post gloas
  • add unit tests + benchmark

AI disclosure

  • created with the help of Claude

@github-actions github-actions Bot added the ssz label Oct 15, 2026
Gloas Transaction is a ProgressiveByteListType, so SerializedListType and
ProgressiveSerializedListType now accept ByteListType | ProgressiveByteListType
elements.

- add ProgressiveByteListType.hashTreeRootFromBytesInto()
- add ByteArrayType.assertValidByteLength() so element sizes are validated
  with the element type's own error
- compare against the exact gloas production type in tests

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Performance Report

🚀🚀 Significant benchmark improvement detected

Benchmark suite Current: aeba2e1 Previous: b7f08f6 Ratio
BeaconState.historicalRoots vc 300000 - deserialize tree 598.00 ns/op 1.8240 us/op 0.33
Full benchmark results
Benchmark suite Current: aeba2e1 Previous: b7f08f6 Ratio
digestTwoHashObjects 50023 times 38.380 ms/op 28.984 ms/op 1.32
digest2Bytes32 50023 times 42.127 ms/op 31.583 ms/op 1.33
digest 50023 times 41.474 ms/op 31.068 ms/op 1.33
input length 32 906.00 ns/op 675.00 ns/op 1.34
input length 64 1.0360 us/op 777.00 ns/op 1.33
input length 128 1.7520 us/op 1.3120 us/op 1.34
input length 256 2.6100 us/op 1.9760 us/op 1.32
input length 512 4.3510 us/op 3.2750 us/op 1.33
input length 1024 8.3460 us/op 6.2810 us/op 1.33
digest 1000000 times 649.06 ms/op 477.00 ms/op 1.36
hashObjectToByteArray 50023 times 980.36 us/op 851.31 us/op 1.15
byteArrayToHashObject 50023 times 1.0775 ms/op 939.75 us/op 1.15
digest64 200092 times 165.04 ms/op 124.74 ms/op 1.32
hash 200092 times using batchHash4UintArray64s 183.72 ms/op 126.76 ms/op 1.45
digest64HashObjects 200092 times 153.88 ms/op 115.56 ms/op 1.33
hash 200092 times using batchHash4HashObjectInputs 164.16 ms/op 107.19 ms/op 1.53
getGindicesAtDepth 3.3320 us/op 2.5830 us/op 1.29
iterateAtDepth 6.0130 us/op 4.6760 us/op 1.29
getGindexBits 373.00 ns/op 245.00 ns/op 1.52
gindexIterator 816.00 ns/op 612.00 ns/op 1.33
HashComputationLevel.push then loop 26.762 ms/op 23.609 ms/op 1.13
HashComputation[] push then loop 40.531 ms/op 35.135 ms/op 1.15
hash 2 32 bytes Uint8Array 500000 times - hashtree 143.97 ms/op 113.87 ms/op 1.26
batch hash 16 x 64 Uint8Array 31250 times - hashtree 24.391 ms/op 20.739 ms/op 1.18
hashTwoObjects 500000 times - hashtree 161.08 ms/op 127.07 ms/op 1.27
executeHashComputations - hashtree 7.6142 ms/op 6.2114 ms/op 1.23
hash 2 32 bytes Uint8Array 500000 times - as-sha256 394.28 ms/op 295.09 ms/op 1.34
batch hash 16 x 64 Uint8Array 31250 times - as-sha256 395.76 ms/op 273.39 ms/op 1.45
hashTwoObjects 500000 times - as-sha256 404.77 ms/op 305.31 ms/op 1.33
executeHashComputations - as-sha256 29.728 ms/op 25.533 ms/op 1.16
hash 2 32 bytes Uint8Array 500000 times - noble 937.67 ms/op 784.10 ms/op 1.20
batch hash 16 x 64 Uint8Array 31250 times - noble 897.42 ms/op 779.36 ms/op 1.15
hashTwoObjects 500000 times - noble 1.3796 s/op 1.2909 s/op 1.07
executeHashComputations - noble 28.829 ms/op 22.048 ms/op 1.31
getHashComputations 1.3432 ms/op 1.5299 ms/op 0.88
executeHashComputations 6.5112 ms/op 6.1803 ms/op 1.05
get root 12.201 ms/op 9.6177 ms/op 1.27
getNodeH() x7812.5 avg hindex 12.214 us/op 10.431 us/op 1.17
getNodeH() x7812.5 index 0 4.6940 us/op 3.8170 us/op 1.23
getNodeH() x7812.5 index 7 4.6900 us/op 3.8140 us/op 1.23
getNodeH() x7812.5 index 7 with key array 4.6890 us/op 3.8140 us/op 1.23
new LeafNode() x7812.5 308.38 us/op 205.53 us/op 1.50
getHashComputations 250000 nodes 9.6707 ms/op 8.8763 ms/op 1.09
batchHash 250000 nodes 61.666 ms/op 56.515 ms/op 1.09
get root 250000 nodes 91.443 ms/op 71.244 ms/op 1.28
getHashComputations 500000 nodes 18.314 ms/op 18.104 ms/op 1.01
batchHash 500000 nodes 153.86 ms/op 141.53 ms/op 1.09
get root 500000 nodes 182.36 ms/op 143.87 ms/op 1.27
getHashComputations 1000000 nodes 38.865 ms/op 36.282 ms/op 1.07
batchHash 1000000 nodes 262.10 ms/op 244.17 ms/op 1.07
get root 1000000 nodes 367.90 ms/op 297.32 ms/op 1.24
multiproof - depth 15, 1 requested leaves 6.5960 us/op 5.1360 us/op 1.28
tree offset multiproof - depth 15, 1 requested leaves 15.560 us/op 11.648 us/op 1.34
compact multiproof - depth 15, 1 requested leaves 2.3950 us/op 2.1810 us/op 1.10
multiproof - depth 15, 2 requested leaves 9.9050 us/op 7.4480 us/op 1.33
tree offset multiproof - depth 15, 2 requested leaves 18.715 us/op 13.769 us/op 1.36
compact multiproof - depth 15, 2 requested leaves 2.3970 us/op 2.1980 us/op 1.09
multiproof - depth 15, 3 requested leaves 14.222 us/op 10.461 us/op 1.36
tree offset multiproof - depth 15, 3 requested leaves 24.378 us/op 17.734 us/op 1.37
compact multiproof - depth 15, 3 requested leaves 2.6960 us/op 2.4070 us/op 1.12
multiproof - depth 15, 4 requested leaves 18.935 us/op 13.781 us/op 1.37
tree offset multiproof - depth 15, 4 requested leaves 30.217 us/op 22.002 us/op 1.37
compact multiproof - depth 15, 4 requested leaves 3.1460 us/op 2.7160 us/op 1.16
packedRootsBytesToLeafNodes bytes 4000 offset 0 4.5700 us/op 3.6730 us/op 1.24
packedRootsBytesToLeafNodes bytes 4000 offset 1 4.5740 us/op 3.6720 us/op 1.25
packedRootsBytesToLeafNodes bytes 4000 offset 2 4.5690 us/op 3.6750 us/op 1.24
packedRootsBytesToLeafNodes bytes 4000 offset 3 4.5700 us/op 3.6780 us/op 1.24
subtreeFillToContents depth 40 count 250000 16.591 ms/op 14.456 ms/op 1.15
setRoot - gindexBitstring 17.560 ms/op 14.449 ms/op 1.22
setRoot - gindex 18.383 ms/op 14.818 ms/op 1.24
getRoot - gindexBitstring 1.9548 ms/op 1.5133 ms/op 1.29
getRoot - gindex 2.5214 ms/op 1.7667 ms/op 1.43
getHashObject then setHashObject 19.371 ms/op 15.501 ms/op 1.25
setNodeWithFn 17.492 ms/op 14.164 ms/op 1.23
getNodeAtDepth depth 0 x100000 215.10 us/op 182.98 us/op 1.18
setNodeAtDepth depth 0 x100000 1.8396 ms/op 1.1207 ms/op 1.64
getNodesAtDepth depth 0 x100000 280.28 us/op 228.70 us/op 1.23
setNodesAtDepth depth 0 x100000 690.54 us/op 565.84 us/op 1.22
getNodeAtDepth depth 1 x100000 280.35 us/op 246.28 us/op 1.14
setNodeAtDepth depth 1 x100000 6.5629 ms/op 5.7900 ms/op 1.13
getNodesAtDepth depth 1 x100000 405.38 us/op 344.98 us/op 1.18
setNodesAtDepth depth 1 x100000 5.5854 ms/op 6.1884 ms/op 0.90
getNodeAtDepth depth 2 x100000 653.31 us/op 490.85 us/op 1.33
setNodeAtDepth depth 2 x100000 13.276 ms/op 10.859 ms/op 1.22
getNodesAtDepth depth 2 x100000 16.430 ms/op 11.501 ms/op 1.43
setNodesAtDepth depth 2 x100000 19.405 ms/op 15.029 ms/op 1.29
tree.getNodesAtDepth - gindexes 5.2297 ms/op 10.604 ms/op 0.49
tree.getNodesAtDepth - push all nodes 1.0069 ms/op 962.86 us/op 1.05
tree.getNodesAtDepth - navigation 154.34 us/op 135.51 us/op 1.14
tree.setNodesAtDepth - indexes 571.88 us/op 440.80 us/op 1.30
set at depth 8 677.00 ns/op 466.00 ns/op 1.45
set at depth 16 981.00 ns/op 712.00 ns/op 1.38
set at depth 32 1.6240 us/op 1.2440 us/op 1.31
iterateNodesAtDepth 8 256 11.992 us/op 9.5910 us/op 1.25
getNodesAtDepth 8 256 3.1580 us/op 2.3940 us/op 1.32
iterateNodesAtDepth 16 65536 3.7202 ms/op 3.0011 ms/op 1.24
getNodesAtDepth 16 65536 949.90 us/op 780.23 us/op 1.22
iterateNodesAtDepth 32 250000 13.963 ms/op 11.110 ms/op 1.26
getNodesAtDepth 32 250000 3.6044 ms/op 2.9672 ms/op 1.21
iterateNodesAtDepth 40 250000 13.908 ms/op 11.144 ms/op 1.25
getNodesAtDepth 40 250000 3.5938 ms/op 2.9877 ms/op 1.20
250000 validators root getter 94.861 ms/op 74.846 ms/op 1.27
250000 validators batchHash() 59.146 ms/op 61.961 ms/op 0.95
250000 validators hashComputations 9.4204 ms/op 12.585 ms/op 0.75
bitlist bytes to struct (120,90) 366.00 ns/op 261.00 ns/op 1.40
bitlist bytes to tree (120,90) 1.6550 us/op 1.5400 us/op 1.07
bitlist bytes to struct (2048,2048) 653.00 ns/op 743.00 ns/op 0.88
bitlist bytes to tree (2048,2048) 2.7860 us/op 3.6000 us/op 0.77
ByteListType - deserialize 6.8666 ms/op 5.9619 ms/op 1.15
BasicListType - deserialize 7.0083 ms/op 7.1913 ms/op 0.97
ByteListType - serialize 7.5242 ms/op 7.3343 ms/op 1.03
BasicListType - serialize 10.862 ms/op 9.9322 ms/op 1.09
BasicListType - tree_convertToStruct 19.224 ms/op 18.603 ms/op 1.03
List[uint8, 68719476736] len 300000 ViewDU.getAll() + iterate 4.1079 ms/op 3.0407 ms/op 1.35
List[uint8, 68719476736] len 300000 ViewDU.get(i) 3.6709 ms/op 2.6653 ms/op 1.38
Array.push len 300000 empty Array - number 3.9263 ms/op 3.8859 ms/op 1.01
Array.set len 300000 from new Array - number 1.1410 ms/op 1.0990 ms/op 1.04
Array.set len 300000 - number 4.1155 ms/op 3.9262 ms/op 1.05
Uint8Array.set len 300000 249.09 us/op 222.51 us/op 1.12
Uint32Array.set len 300000 474.75 us/op 480.03 us/op 0.99
Container({a: uint8, b: uint8}) getViewDU x300000 25.917 ms/op 22.418 ms/op 1.16
ContainerNodeStruct({a: uint8, b: uint8}) getViewDU x300000 9.1934 ms/op 7.5118 ms/op 1.22
List(Container) len 300000 ViewDU.getAllReadonly() + iterate 88.107 ms/op 77.542 ms/op 1.14
List(Container) len 300000 ViewDU.getAllReadonlyValues() + iterate 224.31 ms/op 212.17 ms/op 1.06
List(Container) len 300000 ViewDU.get(i) 7.1415 ms/op 9.1332 ms/op 0.78
List(Container) len 300000 ViewDU.getReadonly(i) 7.1970 ms/op 10.607 ms/op 0.68
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonly() + iterate 14.906 ms/op 12.440 ms/op 1.20
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonlyValues() + iterate 3.3565 ms/op 4.3780 ms/op 0.77
List(ContainerNodeStruct) len 300000 ViewDU.get(i) 6.7881 ms/op 8.3862 ms/op 0.81
List(ContainerNodeStruct) len 300000 ViewDU.getReadonly(i) 6.7635 ms/op 7.7639 ms/op 0.87
Array.push len 300000 empty Array - object 4.1407 ms/op 3.8911 ms/op 1.06
Array.set len 300000 from new Array - object 1.2088 ms/op 927.68 us/op 1.30
Array.set len 300000 - object 4.2780 ms/op 4.0180 ms/op 1.06
List 300 txs of 500 bytes Uint8Array[] deserialize 220.22 us/op
List 300 txs of 500 bytes SerializedList deserialize 97.717 us/op
List 300 txs of 500 bytes Uint8Array[] serialize 64.150 us/op
List 300 txs of 500 bytes SerializedList serialize 52.767 us/op
List 300 txs of 500 bytes Uint8Array[] hashTreeRoot 4.2668 ms/op
List 300 txs of 500 bytes SerializedList hashTreeRoot 4.1873 ms/op
List 1048576 empty txs Uint8Array[] deserialize 137.39 ms/op
List 1048576 empty txs SerializedList deserialize 15.338 ms/op
List 1048576 empty txs Uint8Array[] serialize 41.832 ms/op
List 1048576 empty txs SerializedList serialize 1.2307 ms/op
List 1048576 empty txs Uint8Array[] hashTreeRoot 606.34 ms/op
List 1048576 empty txs SerializedList hashTreeRoot 601.22 ms/op
ProgressiveList 300 txs of 500 bytes Uint8Array[] deserialize 188.78 us/op
ProgressiveList 300 txs of 500 bytes SerializedList deserialize 92.752 us/op
ProgressiveList 300 txs of 500 bytes Uint8Array[] serialize 52.800 us/op
ProgressiveList 300 txs of 500 bytes SerializedList serialize 46.031 us/op
ProgressiveList 300 txs of 500 bytes Uint8Array[] hashTreeRoot 1.4704 ms/op
ProgressiveList 300 txs of 500 bytes SerializedList hashTreeRoot 1.4601 ms/op
ProgressiveList 1048576 empty txs Uint8Array[] deserialize 140.55 ms/op
ProgressiveList 1048576 empty txs SerializedList deserialize 12.257 ms/op
ProgressiveList 1048576 empty txs Uint8Array[] serialize 32.617 ms/op
ProgressiveList 1048576 empty txs SerializedList serialize 736.15 us/op
ProgressiveList 1048576 empty txs Uint8Array[] hashTreeRoot 622.63 ms/op
ProgressiveList 1048576 empty txs SerializedList hashTreeRoot 630.91 ms/op
cachePermanentRootStruct no cache 2.5730 us/op 2.0370 us/op 1.26
cachePermanentRootStruct with cache 161.00 ns/op 123.00 ns/op 1.31
epochParticipation len 250000 rws 7813 2.2338 ms/op 1.8184 ms/op 1.23
Deneb BeaconBlock.hashTreeRoot(), numTransaction=200 4.1830 ms/op 3.2096 ms/op 1.30
BeaconState ViewDU batchHashTreeRoot vc=200000 mod=100000 189.23 ms/op 184.54 ms/op 1.03
BeaconState ViewDU batchHashTreeRoot - commit step vc=200000 mod=100000 153.01 ms/op 145.76 ms/op 1.05
BeaconState ViewDU batchHashTreeRoot - hash step vc=200000 mod=100000 31.143 ms/op 29.384 ms/op 1.06
BeaconState ViewDU hashTreeRoot() vc=200000 mod=100000 635.30 ms/op 534.72 ms/op 1.19
BeaconState ViewDU hashTreeRoot - commit step vc=200000 mod=100000 52.544 ms/op 42.175 ms/op 1.25
BeaconState ViewDU hashTreeRoot - validator tree creation vc=100000 mod=100000 234.49 ms/op 203.66 ms/op 1.15
sweep 16384 balances after clone + commit 22.664 ns/op 18.354 ns/op 1.23
deserialize Attestation - tree 2.9540 us/op 2.5440 us/op 1.16
deserialize Attestation - struct 1.5800 us/op 1.3360 us/op 1.18
deserialize Attestation - struct (reuse bytes) 1.1750 us/op 889.00 ns/op 1.32
deserialize SignedAggregateAndProof - tree 4.1130 us/op 3.4510 us/op 1.19
deserialize SignedAggregateAndProof - struct 2.5290 us/op 2.7490 us/op 0.92
deserialize SignedAggregateAndProof - struct (reuse bytes) 1.5500 us/op 1.2340 us/op 1.26
deserialize SyncCommitteeMessage - tree 1.1610 us/op 939.00 ns/op 1.24
deserialize SyncCommitteeMessage - struct 932.00 ns/op 882.00 ns/op 1.06
deserialize SyncCommitteeMessage - struct (reuse bytes) 586.00 ns/op 442.00 ns/op 1.33
deserialize SignedContributionAndProof - tree 2.4970 us/op 1.9920 us/op 1.25
deserialize SignedContributionAndProof - struct 2.1100 us/op 2.1180 us/op 1.00
deserialize SignedContributionAndProof - struct (reuse bytes) 995.00 ns/op 767.00 ns/op 1.30
deserialize SignedBeaconBlock - tree 232.10 us/op 213.79 us/op 1.09
deserialize SignedBeaconBlock - struct 106.54 us/op 109.19 us/op 0.98
deserialize SignedBeaconBlock - struct (reuse bytes) 64.828 us/op 49.980 us/op 1.30
BeaconState vc 300000 - deserialize tree 342.05 ms/op 278.80 ms/op 1.23
BeaconState vc 300000 - serialize tree 102.43 ms/op 63.425 ms/op 1.61
BeaconState.historicalRoots vc 300000 - deserialize tree 598.00 ns/op 1.8240 us/op 0.33
BeaconState.historicalRoots vc 300000 - serialize tree 531.00 ns/op 567.00 ns/op 0.94
BeaconState.validators vc 300000 - deserialize tree 315.53 ms/op 256.05 ms/op 1.23
BeaconState.validators vc 300000 - serialize tree 42.280 ms/op 34.945 ms/op 1.21
BeaconState.balances vc 300000 - deserialize tree 7.9667 ms/op 6.9049 ms/op 1.15
BeaconState.balances vc 300000 - serialize tree 10.314 ms/op 5.4676 ms/op 1.89
BeaconState.previousEpochParticipation vc 300000 - deserialize tree 695.42 us/op 552.45 us/op 1.26
BeaconState.previousEpochParticipation vc 300000 - serialize tree 608.92 us/op 441.76 us/op 1.38
BeaconState.currentEpochParticipation vc 300000 - deserialize tree 695.01 us/op 551.40 us/op 1.26
BeaconState.currentEpochParticipation vc 300000 - serialize tree 639.61 us/op 498.51 us/op 1.28
BeaconState.inactivityScores vc 300000 - deserialize tree 8.0726 ms/op 23.459 ms/op 0.34
BeaconState.inactivityScores vc 300000 - serialize tree 10.079 ms/op 3.8121 ms/op 2.64
hashTreeRoot Attestation - struct 7.7060 us/op 5.9090 us/op 1.30
hashTreeRoot Attestation - tree 6.9960 us/op 5.3400 us/op 1.31
hashTreeRoot SignedAggregateAndProof - struct 11.044 us/op 8.5640 us/op 1.29
hashTreeRoot SignedAggregateAndProof - tree 10.587 us/op 8.1280 us/op 1.30
hashTreeRoot SyncCommitteeMessage - struct 2.6510 us/op 2.1110 us/op 1.26
hashTreeRoot SyncCommitteeMessage - tree 2.5590 us/op 2.0280 us/op 1.26
hashTreeRoot SignedContributionAndProof - struct 6.6790 us/op 5.3210 us/op 1.26
hashTreeRoot SignedContributionAndProof - tree 7.2950 us/op 5.6190 us/op 1.30
hashTreeRoot SignedBeaconBlock - struct 676.37 us/op 520.68 us/op 1.30
hashTreeRoot SignedBeaconBlock - tree 640.21 us/op 476.45 us/op 1.34
hashTreeRoot Validator - struct 3.4800 us/op 2.6940 us/op 1.29
hashTreeRoot Validator - tree 5.3090 us/op 4.2090 us/op 1.26
BeaconState vc 300000 - hashTreeRoot tree 1.7561 s/op 1.4774 s/op 1.19
BeaconState vc 300000 - batchHashTreeRoot tree 3.3369 s/op 3.0332 s/op 1.10
BeaconState.historicalRoots vc 300000 - hashTreeRoot tree 771.00 ns/op 614.00 ns/op 1.26
BeaconState.validators vc 300000 - hashTreeRoot tree 1.7232 s/op 1.4937 s/op 1.15
BeaconState.balances vc 300000 - hashTreeRoot tree 27.219 ms/op 20.490 ms/op 1.33
BeaconState.previousEpochParticipation vc 300000 - hashTreeRoot tree 3.3922 ms/op 2.6245 ms/op 1.29
BeaconState.currentEpochParticipation vc 300000 - hashTreeRoot tree 3.3981 ms/op 2.5475 ms/op 1.33
BeaconState.inactivityScores vc 300000 - hashTreeRoot tree 27.559 ms/op 20.520 ms/op 1.34
hash64 x18 6.2200 us/op 4.8280 us/op 1.29
hashTwoObjects x18 6.2160 us/op 4.8400 us/op 1.28
hash64 x1740 577.83 us/op 450.66 us/op 1.28
hashTwoObjects x1740 575.92 us/op 451.10 us/op 1.28
hash64 x2700000 908.48 ms/op 718.21 ms/op 1.26
hashTwoObjects x2700000 894.38 ms/op 699.45 ms/op 1.28
get_exitEpoch - ContainerType 175.00 ns/op 133.00 ns/op 1.32
get_exitEpoch - ContainerNodeStructType 176.00 ns/op 136.00 ns/op 1.29
set_exitEpoch - ContainerType 197.00 ns/op 145.00 ns/op 1.36
set_exitEpoch - ContainerNodeStructType 192.00 ns/op 136.00 ns/op 1.41
get_pubkey - ContainerType 734.00 ns/op 630.00 ns/op 1.17
get_pubkey - ContainerNodeStructType 172.00 ns/op 131.00 ns/op 1.31
hashTreeRoot - ContainerType 309.00 ns/op 223.00 ns/op 1.39
hashTreeRoot - ContainerNodeStructType 302.00 ns/op 215.00 ns/op 1.40
createProof - ContainerType 3.0670 us/op 2.4980 us/op 1.23
createProof - ContainerNodeStructType 17.276 us/op 13.545 us/op 1.28
serialize - ContainerType 1.3450 us/op 1.1660 us/op 1.15
serialize - ContainerNodeStructType 922.00 ns/op 814.00 ns/op 1.13
set_exitEpoch_and_hashTreeRoot - ContainerType 2.0720 us/op 1.7000 us/op 1.22
set_exitEpoch_and_hashTreeRoot - ContainerNodeStructType 5.8170 us/op 4.6040 us/op 1.26
doBatchHashTreeRootValidators 3.2350 us/op 2.6380 us/op 1.23
ContainerNodeStructViewDU hashTreeRoot 20.447 us/op 16.280 us/op 1.26
Array - for of 4.8300 us/op 3.0820 us/op 1.57
Array - for(;;) 4.0200 us/op 2.5240 us/op 1.59
basicListValue.readonlyValuesArray() 3.0715 ms/op 2.5581 ms/op 1.20
basicListValue.readonlyValuesArray() + loop all 3.6775 ms/op 3.2373 ms/op 1.14
compositeListValue.readonlyValuesArray() 10.485 ms/op 8.7064 ms/op 1.20
compositeListValue.readonlyValuesArray() + loop all 10.583 ms/op 12.528 ms/op 0.84
Number64UintType - get balances list 2.6294 ms/op 2.3255 ms/op 1.13
Number64UintType - set balances list 10.024 ms/op 7.5189 ms/op 1.33
Number64UintType - get and increase 10 then set 27.453 ms/op 21.951 ms/op 1.25
Number64UintType - increase 10 using applyDelta 14.591 ms/op 11.601 ms/op 1.26
Number64UintType - increase 10 using applyDeltaInBatch 14.574 ms/op 11.636 ms/op 1.25
tree_newTreeFromUint64Deltas 12.011 ms/op 12.096 ms/op 0.99
unsafeUint8ArrayToTree 22.169 ms/op 16.094 ms/op 1.38
bitLength(50) 189.00 ns/op 146.00 ns/op 1.29
bitLengthStr(50) 197.00 ns/op 150.00 ns/op 1.31
bitLength(8000) 189.00 ns/op 146.00 ns/op 1.29
bitLengthStr(8000) 240.00 ns/op 187.00 ns/op 1.28
bitLength(250000) 186.00 ns/op 147.00 ns/op 1.27
bitLengthStr(250000) 274.00 ns/op 217.00 ns/op 1.26
merkleize 32 chunks 10.903 us/op 8.5120 us/op 1.28
merkleizeBlocksBytes 32 chunks 2.6360 us/op 2.1790 us/op 1.21
merkleizeBlockArray 32 chunks 4.8550 us/op 3.8800 us/op 1.25
merkleize 128 chunks 43.388 us/op 33.370 us/op 1.30
merkleizeBlocksBytes 128 chunks 6.6640 us/op 5.5750 us/op 1.20
merkleizeBlockArray 128 chunks 14.202 us/op 11.331 us/op 1.25
merkleize 512 chunks 173.55 us/op 133.21 us/op 1.30
merkleizeBlocksBytes 512 chunks 21.493 us/op 18.214 us/op 1.18
merkleizeBlockArray 512 chunks 49.145 us/op 39.425 us/op 1.25
merkleize 1024 chunks 353.73 us/op 269.63 us/op 1.31
merkleizeBlocksBytes 1024 chunks 40.846 us/op 34.710 us/op 1.18
merkleizeBlockArray 1024 chunks 95.456 us/op 76.394 us/op 1.25
floor - Math.floor (53) 0.34998 ns/op 0.30686 ns/op 1.14
floor - << 0 (53) 0.35001 ns/op 0.30680 ns/op 1.14
floor - Math.floor (512) 0.35009 ns/op 0.30683 ns/op 1.14
floor - << 0 (512) 0.35016 ns/op 0.30679 ns/op 1.14
fnIf(0) 1.4985 ns/op 1.2275 ns/op 1.22
fnSwitch(0) 2.4882 ns/op 1.9853 ns/op 1.25
fnObj(0) 0.35001 ns/op 0.30683 ns/op 1.14
fnArr(0) 0.35007 ns/op 0.30684 ns/op 1.14
fnIf(4) 2.1747 ns/op 1.9084 ns/op 1.14
fnSwitch(4) 2.1756 ns/op 1.9061 ns/op 1.14
fnObj(4) 0.35002 ns/op 0.30678 ns/op 1.14
fnArr(4) 0.35007 ns/op 0.30682 ns/op 1.14
fnIf(9) 3.1096 ns/op 2.5902 ns/op 1.20
fnSwitch(9) 2.1770 ns/op 1.9010 ns/op 1.15
fnObj(9) 0.35007 ns/op 0.30682 ns/op 1.14
fnArr(9) 0.35004 ns/op 0.30684 ns/op 1.14
Container {a,b,vec} - as struct x100000 34.830 us/op 30.567 us/op 1.14
Container {a,b,vec} - as tree x100000 653.35 us/op 491.12 us/op 1.33
Container {a,vec,b} - as struct x100000 77.218 us/op 67.783 us/op 1.14
Container {a,vec,b} - as tree x100000 995.54 us/op 764.82 us/op 1.30
get 2 props x1000000 - rawObject 311.19 us/op 272.70 us/op 1.14
get 2 props x1000000 - proxy 77.819 ms/op 63.182 ms/op 1.23
get 2 props x1000000 - customObj 311.16 us/op 272.78 us/op 1.14
Simple object binary -> struct 445.00 ns/op 334.00 ns/op 1.33
Simple object binary -> tree_backed 931.00 ns/op 725.00 ns/op 1.28
Simple object struct -> tree_backed 1.4080 us/op 1.2120 us/op 1.16
Simple object tree_backed -> struct 1.2430 us/op 1.0240 us/op 1.21
Simple object struct -> binary 694.00 ns/op 636.00 ns/op 1.09
Simple object tree_backed -> binary 1.0670 us/op 884.00 ns/op 1.21
aggregationBits binary -> struct 376.00 ns/op 271.00 ns/op 1.39
aggregationBits binary -> tree_backed 1.6580 us/op 1.6600 us/op 1.00
aggregationBits struct -> tree_backed 1.9020 us/op 2.1100 us/op 0.90
aggregationBits tree_backed -> struct 799.00 ns/op 732.00 ns/op 1.09
aggregationBits struct -> binary 584.00 ns/op 487.00 ns/op 1.20
aggregationBits tree_backed -> binary 700.00 ns/op 622.00 ns/op 1.13
List(uint8) 100000 binary -> struct 702.63 us/op 622.29 us/op 1.13
List(uint8) 100000 binary -> tree_backed 231.43 us/op 185.80 us/op 1.25
List(uint8) 100000 struct -> tree_backed 1.4794 ms/op 1.2309 ms/op 1.20
List(uint8) 100000 tree_backed -> struct 808.80 us/op 681.16 us/op 1.19
List(uint8) 100000 struct -> binary 1.3484 ms/op 1.0442 ms/op 1.29
List(uint8) 100000 tree_backed -> binary 246.85 us/op 130.73 us/op 1.89
List(uint64Number) 100000 binary -> struct 1.0470 ms/op 845.56 us/op 1.24
List(uint64Number) 100000 binary -> tree_backed 1.9491 ms/op 1.5436 ms/op 1.26
List(uint64Number) 100000 struct -> tree_backed 3.6576 ms/op 2.9000 ms/op 1.26
List(uint64Number) 100000 tree_backed -> struct 1.9837 ms/op 1.3853 ms/op 1.43
List(uint64Number) 100000 struct -> binary 2.4622 ms/op 1.9929 ms/op 1.24
List(uint64Number) 100000 tree_backed -> binary 902.16 us/op 1.2693 ms/op 0.71
List(Uint64Bigint) 100000 binary -> struct 1.5729 ms/op 1.3244 ms/op 1.19
List(Uint64Bigint) 100000 binary -> tree_backed 1.9563 ms/op 1.5552 ms/op 1.26
List(Uint64Bigint) 100000 struct -> tree_backed 3.4765 ms/op 2.7806 ms/op 1.25
List(Uint64Bigint) 100000 tree_backed -> struct 2.5265 ms/op 1.8916 ms/op 1.34
List(Uint64Bigint) 100000 struct -> binary 2.2794 ms/op 1.7805 ms/op 1.28
List(Uint64Bigint) 100000 tree_backed -> binary 2.0435 ms/op 1.1121 ms/op 1.84
Vector(Root) 100000 binary -> struct 10.841 ms/op 9.4440 ms/op 1.15
Vector(Root) 100000 binary -> tree_backed 18.112 ms/op 15.193 ms/op 1.19
Vector(Root) 100000 struct -> tree_backed 21.560 ms/op 17.124 ms/op 1.26
Vector(Root) 100000 tree_backed -> struct 15.335 ms/op 11.837 ms/op 1.30
Vector(Root) 100000 struct -> binary 6.0467 ms/op 4.4233 ms/op 1.37
Vector(Root) 100000 tree_backed -> binary 5.2435 ms/op 3.5783 ms/op 1.47
List(Validator) 100000 binary -> struct 66.000 ms/op 54.950 ms/op 1.20
List(Validator) 100000 binary -> tree_backed 187.31 ms/op 162.85 ms/op 1.15
List(Validator) 100000 struct -> tree_backed 216.66 ms/op 185.70 ms/op 1.17
List(Validator) 100000 tree_backed -> struct 142.26 ms/op 136.72 ms/op 1.04
List(Validator) 100000 struct -> binary 43.964 ms/op 37.395 ms/op 1.18
List(Validator) 100000 tree_backed -> binary 69.914 ms/op 78.061 ms/op 0.90
List(Validator-NS) 100000 binary -> struct 73.162 ms/op 54.998 ms/op 1.33
List(Validator-NS) 100000 binary -> tree_backed 105.93 ms/op 83.653 ms/op 1.27
List(Validator-NS) 100000 struct -> tree_backed 133.50 ms/op 95.853 ms/op 1.39
List(Validator-NS) 100000 tree_backed -> struct 110.10 ms/op 80.123 ms/op 1.37
List(Validator-NS) 100000 struct -> binary 13.618 ms/op 10.804 ms/op 1.26
List(Validator-NS) 100000 tree_backed -> binary 18.249 ms/op 19.205 ms/op 0.95
get epochStatuses - MutableVector 137.39 us/op 108.05 us/op 1.27
get epochStatuses - ViewDU 168.88 us/op 135.36 us/op 1.25
set epochStatuses - ListTreeView 1.8582 ms/op 1.4224 ms/op 1.31
set epochStatuses - ListTreeView - set() 483.02 us/op 291.34 us/op 1.66
set epochStatuses - ListTreeView - commit() 761.73 us/op 578.87 us/op 1.32
bitstring 461.70 ns/op 378.99 ns/op 1.22
bit mask 7.1947 ns/op 6.3222 ns/op 1.14
struct - increase slot to 1000000 350.22 us/op 306.76 us/op 1.14
UintNumberType - increase slot to 1000000 29.290 ms/op 23.496 ms/op 1.25
UintBigintType - increase slot to 1000000 161.20 ms/op 125.51 ms/op 1.28
UintBigint8 x 100000 tree_deserialize 5.0622 ms/op 3.5331 ms/op 1.43
UintBigint8 x 100000 tree_serialize 1.2479 ms/op 961.43 us/op 1.30
UintBigint16 x 100000 tree_deserialize 5.0787 ms/op 3.5575 ms/op 1.43
UintBigint16 x 100000 tree_serialize 1.2771 ms/op 992.24 us/op 1.29
UintBigint32 x 100000 tree_deserialize 4.8293 ms/op 3.7839 ms/op 1.28
UintBigint32 x 100000 tree_serialize 1.6057 ms/op 1.3251 ms/op 1.21
UintBigint64 x 100000 tree_deserialize 5.2918 ms/op 4.4500 ms/op 1.19
UintBigint64 x 100000 tree_serialize 1.5783 ms/op 1.2827 ms/op 1.23
UintBigint8 x 100000 value_deserialize 404.46 us/op 327.34 us/op 1.24
UintBigint8 x 100000 value_serialize 380.34 us/op 312.44 us/op 1.22
UintBigint16 x 100000 value_deserialize 466.82 us/op 354.60 us/op 1.32
UintBigint16 x 100000 value_serialize 365.83 us/op 335.28 us/op 1.09
UintBigint32 x 100000 value_deserialize 466.76 us/op 381.99 us/op 1.22
UintBigint32 x 100000 value_serialize 407.46 us/op 378.31 us/op 1.08
UintBigint64 x 100000 value_deserialize 528.72 us/op 409.27 us/op 1.29
UintBigint64 x 100000 value_serialize 524.23 us/op 509.75 us/op 1.03
UintBigint8 x 100000 deserialize 1.9210 ms/op 1.7365 ms/op 1.11
UintBigint8 x 100000 serialize 1.4864 ms/op 1.2250 ms/op 1.21
UintBigint16 x 100000 deserialize 2.0238 ms/op 1.8034 ms/op 1.12
UintBigint16 x 100000 serialize 2.8819 ms/op 1.2148 ms/op 2.37
UintBigint32 x 100000 deserialize 2.0539 ms/op 1.7055 ms/op 1.20
UintBigint32 x 100000 serialize 2.7467 ms/op 2.1391 ms/op 1.28
UintBigint64 x 100000 deserialize 1.9283 ms/op 1.7156 ms/op 1.12
UintBigint64 x 100000 serialize 1.5534 ms/op 1.0548 ms/op 1.47
UintBigint128 x 100000 deserialize 2.3065 ms/op 2.0226 ms/op 1.14
UintBigint128 x 100000 serialize 11.939 ms/op 8.9411 ms/op 1.34
UintBigint256 x 100000 deserialize 3.0233 ms/op 2.5570 ms/op 1.18
UintBigint256 x 100000 serialize 37.592 ms/op 28.896 ms/op 1.30
Slice from Uint8Array x25000 941.71 us/op 728.33 us/op 1.29
Slice from ArrayBuffer x25000 10.417 ms/op 11.236 ms/op 0.93
Slice from ArrayBuffer x25000 + new Uint8Array 16.727 ms/op 12.859 ms/op 1.30
Copy Uint8Array 100000 iterate 934.27 us/op 706.92 us/op 1.32
Copy Uint8Array 100000 slice 385.21 us/op 391.85 us/op 0.98
Copy Uint8Array 100000 Uint8Array.prototype.slice.call 81.021 us/op 56.455 us/op 1.44
Copy Buffer 100000 Uint8Array.prototype.slice.call 82.127 us/op 53.998 us/op 1.52
Copy Uint8Array 100000 slice + set 163.40 us/op 117.66 us/op 1.39
Copy Uint8Array 100000 subarray + set 79.973 us/op 51.378 us/op 1.56
Copy Uint8Array 100000 slice arrayBuffer 80.304 us/op 51.773 us/op 1.55
Uint64 deserialize 100000 - iterate Uint8Array 1.2452 ms/op 1.1839 ms/op 1.05
Uint64 deserialize 100000 - by Uint32A 1.2496 ms/op 1.1810 ms/op 1.06
Uint64 deserialize 100000 - by DataView.getUint32 x2 1.4085 ms/op 1.2455 ms/op 1.13
Uint64 deserialize 100000 - by DataView.getBigUint64 3.6634 ms/op 3.0036 ms/op 1.22
Uint64 deserialize 100000 - by byte 15.632 ms/op 12.014 ms/op 1.30

by benchmarkbot/action

@twoeths
twoeths marked this pull request as ready for review October 16, 2026 08:22
@twoeths
twoeths requested a review from a team as a code owner October 16, 2026 08:22
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T08:25:17.351511Z 3d0e0fd Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

wemeetagain pushed a commit that referenced this pull request Oct 16, 2026
…lements (#556)

**Motivation**

`readOffsetsArrayComposite()` returned one offset per element, so both
deserialize loops in `arrayComposite.ts` had to special case the last
element (`i === length - 1 ? end : start + offsets[i + 1]`). The
comments next to them ("Capture length before pushing end offset",
"offests include the last element end") describe an n + 1 layout that
the code no longer had.

The fixed size branch also built an offsets array of `i * fixedSize`
just so both element kinds could share one loop, while serialization
already writes fixed size elements at `offset + i * fixedSize` directly.

This also lets #555 drop the `withEndOffset` flag it adds to
`readVariableOffsetsArrayComposite()`, since that is the layout
`SerializedList` needs.

**Description**

- Fixed size elements are read at `start + i * fixedSize` in
`value_deserializeFromBytesArrayComposite()`,
`tree_deserializeFromBytesArrayComposite()` and
`ProgressiveListCompositeType.tree_deserializeFromBytes()`, with no
offsets array. The length checks (`size % fixedSize`,
`assertValidArrayLength()`) and their errors are unchanged
- `readVariableOffsetsArrayComposite()` now returns n + 1 offsets, with
a trailing entry equal to `end - start`. An empty array returns `[0]`
- `assertValidArrayLength()` still gets the element count
(`offsets.length - 1`)
- The variable size loops use `endEl = start + offsets[i + 1]` with no
branch

The offset readers are module private and the deserialize functions
above are their only callers. ProgressiveList keeps its own variable
size offset reader. Validation and error order are unchanged. The
trailing write happens after all offset checks.

Fixed size elements use plain number math, so they don't wrap at any
size. Variable size offsets stay in a `Uint32Array`, except when the
array is 2^32 bytes or larger. Then they go in a `Float64Array` so the
trailing entry doesn't wrap, and the last element still ends at `end` as
before. The SSZ spec caps the serialized size below 2^32, but the
library accepted these inputs before this change.

**Tests**

- `pnpm test:unit`: 1814 passed
- `pnpm test:spec-generic`: 5065 passed
- `pnpm test:spec-static-minimal`: 13284 passed, `pnpm
test:spec-static-mainnet`: 540 passed
- `pnpm lint` clean

**Benchmarks**

Local, node 24 (V8 13.6), at 08422c6. Medians of 6 interleaved
master/branch rounds per case, with the side that runs first alternating
each round. `deserialize()` = value, `deserializeToView()` = tree. Cases
up to 128 elements come from the small file (3 to 6 s per case), the
rest from the large file (1.5 to 4 s per case).

| case | value master | value branch | tree master | tree branch |
| --- | --- | --- | --- | --- |
| List[ByteList] 1 x 100B | 0.91 us | 0.90 us | 2.83 us | 2.73 us |
| List[ByteList] 4 x 100B | 3.08 us | 2.99 us | 7.07 us | 6.84 us |
| List[ByteList] 1000 x 1000B | 1.10 ms | 1.06 ms | 3.71 ms | 3.79 ms |
| List[ByteList] 10000 x 100B | 9.14 ms | 8.51 ms | 24.66 ms | 20.82 ms
|
| List[ByteList] 100000 x 0B | 8.46 ms | 8.99 ms | 34.71 ms | 27.17 ms |
| ProgressiveList[ProgressiveByteList] 10000 x 100B | 8.98 ms | 8.72 ms
| - | - |
| List[Container(fixed 40B)] 1 | 0.52 us | 0.48 us | 2.69 us | 2.54 us |
| List[Container(fixed 40B)] 16 | 2.20 us | 2.11 us | 6.73 us | 6.23 us
|
| List[Container(fixed 40B)] 128 | 14.15 us | 13.01 us | 37.04 us |
35.58 us |
| List[Container(fixed 40B)] 100000 | 23.81 ms | 26.08 ms | 50.51 ms |
47.99 ms |
| ProgressiveList[Container(fixed 40B)] 16 | 2.50 us | 2.19 us | 5.85 us
| 5.19 us |
| ProgressiveList[Container(fixed 40B)] 100000 | 23.39 ms | 22.52 ms |
51.03 ms | 45.75 ms |
| Vector[Bytes32, 4] | 0.63 us | 0.57 us | 0.98 us | 0.91 us |
| Vector[Bytes32, 8192] | 453.48 us | 413.21 us | 1.15 ms | 960.27 us |

The fixed size cases are a few percent faster than master because
nothing is allocated for offsets anymore. This also removes the n = 16
regression the first commit had (`List[Container(fixed 40B)] 16` value:
2.16 us on master vs 2.89 us, from `Uint32Array(17)` crossing V8's 64
byte on heap limit). It's now 2.20 us on master vs 2.11 us. Variable
size cases are unchanged within noise. On this machine the large cases
vary by about 20% between rounds of the same code, so differences below
that only show up in the small cases.

<details><summary>Bench file (not committed). The small cases used the
same file with counts 1/4 (ByteList), 1/16/128 (Container) and
Vector[Bytes32, 4], without the ProgressiveList cases</summary>

```ts
import {bench, describe} from "@chainsafe/benchmark";
import {
  ByteListType,
  ByteVectorType,
  CompositeType,
  ContainerType,
  ListCompositeType,
  ProgressiveByteListType,
  ProgressiveListCompositeType,
  UintNumberType,
  VectorCompositeType,
} from "../../../src/index.ts";

const bytes32 = new ByteVectorType(32);
const uint64 = new UintNumberType(8);
const byteList = new ByteListType(2 ** 30);
const fixedContainer = new ContainerType({root: bytes32, value: uint64}, {typeName: "Container(fixed 40B)"});

function filledBytes(len: number, seed: number): Uint8Array {
  const b = new Uint8Array(len);
  for (let i = 0; i < len; i++) b[i] = (seed + i) & 0xff;
  return b;
}

function byteLists(count: number, size: number): Uint8Array[] {
  return Array.from({length: count}, (_, i) => filledBytes(size, i));
}

describe("arrayComposite deserialize", () => {
  const cases: {id: string; type: CompositeType<unknown, unknown, unknown>; value: unknown; tree: boolean}[] = [];

  // variable size elements, readVariableOffsetsArrayComposite()
  for (const [count, size] of [
    [4, 100],
    [1_000, 1_000],
    [10_000, 100],
    [100_000, 0],
  ] as const) {
    cases.push({
      id: `List[ByteList] ${count} x ${size}B`,
      type: new ListCompositeType(byteList, 2 ** 20),
      value: byteLists(count, size),
      tree: true,
    });
  }
  cases.push({
    id: "ProgressiveList[ProgressiveByteList] 10000 x 100B",
    type: new ProgressiveListCompositeType(new ProgressiveByteListType()),
    value: byteLists(10_000, 100),
    tree: false,
  });

  // fixed size elements, start + i * fixedSize
  for (const count of [16, 100_000]) {
    cases.push({
      id: `List[Container(fixed 40B)] ${count}`,
      type: new ListCompositeType(fixedContainer, 2 ** 40),
      value: Array.from({length: count}, (_, i) => ({root: filledBytes(32, i), value: i})),
      tree: true,
    });
  }
  for (const count of [16, 100_000]) {
    cases.push({
      id: `ProgressiveList[Container(fixed 40B)] ${count}`,
      type: new ProgressiveListCompositeType(fixedContainer),
      value: Array.from({length: count}, (_, i) => ({root: filledBytes(32, i), value: i})),
      tree: true,
    });
  }
  cases.push({
    id: "Vector[Bytes32, 8192]",
    type: new VectorCompositeType(bytes32, 8192),
    value: Array.from({length: 8192}, (_, i) => filledBytes(32, i)),
    tree: true,
  });

  for (const {id, type, value, tree} of cases) {
    const bytes = type.serialize(value);

    bench({
      id: `${id} - value`,
      fn: () => {
        type.deserialize(bytes);
      },
    });

    if (tree) {
      bench({
        id: `${id} - tree`,
        fn: () => {
          type.deserializeToView(bytes);
        },
      });
    }
  }
});
```

</details>

🤖 Generated with AI assistance

---------

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
lodekeeper and others added 2 commits October 16, 2026 17:39
**Motivation**

#556 is merged and conflicts with this branch in
`readVariableOffsetsArrayComposite()`.

**Description**

Merges `master` into `te/serialized_list`:

- `readVariableOffsetsArrayComposite()` takes master's version and stays
exported. It always returns n + 1 offsets now, so `withEndOffset` is
gone. Offsets are a `Float64Array` when the list is 2^32 bytes or
larger, so the trailing entry doesn't wrap
- `SerializedListBaseType.value_deserializeFromBytes()` drops the `true`
argument
- `SerializedList.offsets` is `Uint32Array | Float64Array` to match. Its
doc comment no longer says that list types keep only n offsets

If you'd rather keep `offsets` a plain `Uint32Array`, the alternative is
to reject serialized lists of 2^32 bytes or more in
`SerializedListBaseType`. That would differ from `ListCompositeType`,
which accepts them.

**Tests**

- `pnpm test:unit`: 2405 passed
- `pnpm test:spec-generic`: 5065 passed
- `pnpm test:spec-static-minimal`: 13284 passed, `pnpm
test:spec-static-mainnet`: 540 passed
- `pnpm lint` clean

🤖 Generated with AI assistance

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
PR #557 brought master's changes into this branch with a squash commit, so the PR comparison still includes the changes from #556. Record the actual merge with master to correct the comparison base.

The resulting tree is identical to ca973a6; preserve the exported readVariableOffsetsArrayComposite helper required by SerializedListType.

@wemeetagain wemeetagain left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, my only issues got resolved in #556

@wemeetagain
wemeetagain merged commit 264c540 into master Oct 16, 2026
12 checks passed
@wemeetagain
wemeetagain deleted the te/serialized_list branch October 16, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants