faster-eth-abi 5.2.1__cp313-cp313-macosx_11_0_arm64.whl → 5.2.20__cp313-cp313-macosx_11_0_arm64.whl

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.

Potentially problematic release.


This version of faster-eth-abi might be problematic. Click here for more details.

Files changed (43) hide show
  1. faster_eth_abi/_codec.cpython-313-darwin.so +0 -0
  2. faster_eth_abi/_codec.py +83 -0
  3. faster_eth_abi/_decoding.cpython-313-darwin.so +0 -0
  4. faster_eth_abi/_decoding.py +299 -0
  5. faster_eth_abi/_encoding.cpython-313-darwin.so +0 -0
  6. faster_eth_abi/_encoding.py +251 -0
  7. faster_eth_abi/_grammar.cpython-313-darwin.so +0 -0
  8. faster_eth_abi/_grammar.py +375 -0
  9. faster_eth_abi/abi.cpython-313-darwin.so +0 -0
  10. faster_eth_abi/base.py +5 -1
  11. faster_eth_abi/codec.py +2694 -52
  12. faster_eth_abi/constants.cpython-313-darwin.so +0 -0
  13. faster_eth_abi/decoding.py +263 -242
  14. faster_eth_abi/encoding.py +201 -154
  15. faster_eth_abi/exceptions.py +26 -14
  16. faster_eth_abi/from_type_str.cpython-313-darwin.so +0 -0
  17. faster_eth_abi/from_type_str.py +7 -1
  18. faster_eth_abi/grammar.py +30 -325
  19. faster_eth_abi/io.py +5 -1
  20. faster_eth_abi/packed.cpython-313-darwin.so +0 -0
  21. faster_eth_abi/packed.py +4 -0
  22. faster_eth_abi/registry.py +201 -83
  23. faster_eth_abi/tools/__init__.cpython-313-darwin.so +0 -0
  24. faster_eth_abi/tools/_strategies.cpython-313-darwin.so +0 -0
  25. faster_eth_abi/tools/_strategies.py +12 -6
  26. faster_eth_abi/typing.py +4627 -0
  27. faster_eth_abi/utils/__init__.cpython-313-darwin.so +0 -0
  28. faster_eth_abi/utils/numeric.cpython-313-darwin.so +0 -0
  29. faster_eth_abi/utils/numeric.py +51 -20
  30. faster_eth_abi/utils/padding.cpython-313-darwin.so +0 -0
  31. faster_eth_abi/utils/string.cpython-313-darwin.so +0 -0
  32. faster_eth_abi/utils/validation.cpython-313-darwin.so +0 -0
  33. faster_eth_abi/utils/validation.py +1 -5
  34. faster_eth_abi-5.2.20.dist-info/METADATA +136 -0
  35. faster_eth_abi-5.2.20.dist-info/RECORD +46 -0
  36. faster_eth_abi-5.2.20.dist-info/top_level.txt +2 -0
  37. faster_eth_abi__mypyc.cpython-313-darwin.so +0 -0
  38. a1f8aa123fabc88e2b56__mypyc.cpython-313-darwin.so +0 -0
  39. faster_eth_abi-5.2.1.dist-info/METADATA +0 -95
  40. faster_eth_abi-5.2.1.dist-info/RECORD +0 -38
  41. faster_eth_abi-5.2.1.dist-info/licenses/LICENSE +0 -21
  42. faster_eth_abi-5.2.1.dist-info/top_level.txt +0 -3
  43. {faster_eth_abi-5.2.1.dist-info → faster_eth_abi-5.2.20.dist-info}/WHEEL +0 -0
@@ -1,18 +1,32 @@
1
+ """Classes for ABI encoding logic.
2
+
3
+ Implements classes and functions for serializing Python values into binary data
4
+ according to ABI type specifications.
5
+ """
1
6
  import abc
2
7
  import codecs
3
8
  import decimal
4
- from itertools import (
5
- accumulate,
9
+ from functools import (
10
+ cached_property,
11
+ lru_cache,
12
+ )
13
+ from types import (
14
+ MethodType,
6
15
  )
7
16
  from typing import (
8
17
  Any,
18
+ Callable,
19
+ ClassVar,
20
+ Final,
21
+ NoReturn,
9
22
  Optional,
23
+ Sequence,
10
24
  Tuple,
11
25
  Type,
26
+ final,
12
27
  )
13
28
 
14
29
  from faster_eth_utils import (
15
- int_to_big_endian,
16
30
  is_address,
17
31
  is_boolean,
18
32
  is_bytes,
@@ -22,7 +36,22 @@ from faster_eth_utils import (
22
36
  is_text,
23
37
  to_canonical_address,
24
38
  )
39
+ from typing_extensions import (
40
+ Self,
41
+ )
25
42
 
43
+ from faster_eth_abi._encoding import (
44
+ encode_elements,
45
+ encode_elements_dynamic,
46
+ encode_fixed,
47
+ encode_signed,
48
+ encode_tuple,
49
+ encode_tuple_all_dynamic,
50
+ encode_tuple_no_dynamic,
51
+ encode_tuple_no_dynamic_funcs,
52
+ int_to_big_endian,
53
+ validate_tuple,
54
+ )
26
55
  from faster_eth_abi.base import (
27
56
  BaseCoder,
28
57
  )
@@ -45,8 +74,6 @@ from faster_eth_abi.utils.numeric import (
45
74
  compute_unsigned_integer_bounds,
46
75
  )
47
76
  from faster_eth_abi.utils.padding import (
48
- fpad,
49
- zpad,
50
77
  zpad_right,
51
78
  )
52
79
  from faster_eth_abi.utils.string import (
@@ -69,7 +96,7 @@ class BaseEncoder(BaseCoder, metaclass=abc.ABCMeta):
69
96
  """
70
97
 
71
98
  @abc.abstractmethod
72
- def validate_value(self, value: Any) -> None: # pragma: no cover
99
+ def validate_value(self, value: Any) -> None:
73
100
  """
74
101
  Checks whether or not the given value can be encoded by this encoder.
75
102
  If the given value cannot be encoded, must raise
@@ -82,7 +109,7 @@ class BaseEncoder(BaseCoder, metaclass=abc.ABCMeta):
82
109
  value: Any,
83
110
  exc: Type[Exception] = EncodingTypeError,
84
111
  msg: Optional[str] = None,
85
- ) -> None:
112
+ ) -> NoReturn:
86
113
  """
87
114
  Throws a standard exception for when a value is not encodable by an
88
115
  encoder.
@@ -102,57 +129,51 @@ class TupleEncoder(BaseEncoder):
102
129
  def __init__(self, encoders: Tuple[BaseEncoder, ...], **kwargs: Any) -> None:
103
130
  super().__init__(encoders=encoders, **kwargs)
104
131
 
105
- self.is_dynamic = any(getattr(e, "is_dynamic", False) for e in self.encoders)
132
+ self._is_dynamic: Final = tuple(
133
+ getattr(e, "is_dynamic", False) for e in self.encoders
134
+ )
135
+ self.is_dynamic = any(self._is_dynamic)
106
136
 
107
- def validate(self):
137
+ validators = []
138
+ for encoder in self.encoders:
139
+ try:
140
+ validator = encoder.validate_value
141
+ except AttributeError:
142
+ validators.append(encoder)
143
+ else:
144
+ validators.append(validator)
145
+
146
+ self.validators: Final[Callable[[Any], None]] = tuple(validators)
147
+
148
+ if type(self).encode is TupleEncoder.encode:
149
+ encode_func = (
150
+ encode_tuple_all_dynamic
151
+ if all(self._is_dynamic)
152
+ else encode_tuple_no_dynamic_funcs.get(
153
+ len(self.encoders),
154
+ encode_tuple_no_dynamic,
155
+ )
156
+ if not self.is_dynamic
157
+ else encode_tuple
158
+ )
159
+
160
+ self.encode = MethodType(encode_func, self)
161
+
162
+ def validate(self) -> None:
108
163
  super().validate()
109
164
 
110
165
  if self.encoders is None:
111
166
  raise ValueError("`encoders` may not be none")
112
167
 
113
- def validate_value(self, value):
114
- if not is_list_like(value):
115
- self.invalidate_value(
116
- value,
117
- msg="must be list-like object such as array or tuple",
118
- )
119
-
120
- if len(value) != len(self.encoders):
121
- self.invalidate_value(
122
- value,
123
- exc=ValueOutOfBounds,
124
- msg=f"value has {len(value)} items when {len(self.encoders)} "
125
- "were expected",
126
- )
127
-
128
- for item, encoder in zip(value, self.encoders):
129
- try:
130
- encoder.validate_value(item)
131
- except AttributeError:
132
- encoder(item)
133
-
134
- def encode(self, values):
135
- self.validate_value(values)
168
+ @final
169
+ def validate_value(self, value: Sequence[Any]) -> None:
170
+ validate_tuple(self, value)
136
171
 
137
- raw_head_chunks = []
138
- tail_chunks = []
139
- for value, encoder in zip(values, self.encoders):
140
- if getattr(encoder, "is_dynamic", False):
141
- raw_head_chunks.append(None)
142
- tail_chunks.append(encoder(value))
143
- else:
144
- raw_head_chunks.append(encoder(value))
145
- tail_chunks.append(b"")
146
-
147
- head_length = sum(32 if item is None else len(item) for item in raw_head_chunks)
148
- tail_offsets = (0,) + tuple(accumulate(map(len, tail_chunks[:-1])))
149
- head_chunks = tuple(
150
- encode_uint_256(head_length + offset) if chunk is None else chunk
151
- for chunk, offset in zip(raw_head_chunks, tail_offsets)
152
- )
172
+ def encode(self, values: Sequence[Any]) -> bytes:
173
+ return encode_tuple(self, values)
153
174
 
154
- encoded_value = b"".join(head_chunks + tuple(tail_chunks))
155
- return encoded_value
175
+ def __call__(self, values: Sequence[Any]) -> bytes:
176
+ return self.encode(values)
156
177
 
157
178
  @parse_tuple_type_str
158
179
  def from_type_str(cls, abi_type, registry):
@@ -170,42 +191,39 @@ class FixedSizeEncoder(BaseEncoder):
170
191
  type_check_fn = None
171
192
  is_big_endian = None
172
193
 
173
- def validate(self):
194
+ def validate(self) -> None:
174
195
  super().validate()
175
196
 
176
- if self.value_bit_size is None:
197
+ value_bit_size = self.value_bit_size
198
+ if value_bit_size is None:
177
199
  raise ValueError("`value_bit_size` may not be none")
178
- if self.data_byte_size is None:
200
+ data_byte_size = self.data_byte_size
201
+ if data_byte_size is None:
179
202
  raise ValueError("`data_byte_size` may not be none")
180
203
  if self.encode_fn is None:
181
204
  raise ValueError("`encode_fn` may not be none")
182
205
  if self.is_big_endian is None:
183
206
  raise ValueError("`is_big_endian` may not be none")
184
207
 
185
- if self.value_bit_size % 8 != 0:
208
+ if value_bit_size % 8 != 0:
186
209
  raise ValueError(
187
- f"Invalid value bit size: {self.value_bit_size}. "
188
- "Must be a multiple of 8"
210
+ f"Invalid value bit size: {value_bit_size}. Must be a multiple of 8"
189
211
  )
190
212
 
191
- if self.value_bit_size > self.data_byte_size * 8:
213
+ if value_bit_size > data_byte_size * 8:
192
214
  raise ValueError("Value byte size exceeds data size")
193
215
 
194
- def validate_value(self, value):
216
+ def validate_value(self, value: Any) -> None:
195
217
  raise NotImplementedError("Must be implemented by subclasses")
196
218
 
197
- def encode(self, value):
219
+ def encode(self, value: Any) -> bytes:
198
220
  self.validate_value(value)
199
- if self.encode_fn is None:
221
+ encode_fn = self.encode_fn
222
+ if encode_fn is None:
200
223
  raise AssertionError("`encode_fn` is None")
201
- base_encoded_value = self.encode_fn(value)
224
+ return encode_fixed(value, encode_fn, self.is_big_endian, self.data_byte_size)
202
225
 
203
- if self.is_big_endian:
204
- padded_encoded_value = zpad(base_encoded_value, self.data_byte_size)
205
- else:
206
- padded_encoded_value = zpad_right(base_encoded_value, self.data_byte_size)
207
-
208
- return padded_encoded_value
226
+ __call__ = encode
209
227
 
210
228
 
211
229
  class Fixed32ByteSizeEncoder(FixedSizeEncoder):
@@ -217,12 +235,12 @@ class BooleanEncoder(Fixed32ByteSizeEncoder):
217
235
  is_big_endian = True
218
236
 
219
237
  @classmethod
220
- def validate_value(cls, value):
238
+ def validate_value(cls, value: Any) -> None:
221
239
  if not is_boolean(value):
222
240
  cls.invalidate_value(value)
223
241
 
224
242
  @classmethod
225
- def encode_fn(cls, value):
243
+ def encode_fn(cls, value: bool) -> bytes:
226
244
  if value is True:
227
245
  return b"\x01"
228
246
  elif value is False:
@@ -245,7 +263,7 @@ class NumberEncoder(Fixed32ByteSizeEncoder):
245
263
  illegal_value_fn = None
246
264
  type_check_fn = None
247
265
 
248
- def validate(self):
266
+ def validate(self) -> None:
249
267
  super().validate()
250
268
 
251
269
  if self.bounds_fn is None:
@@ -253,15 +271,15 @@ class NumberEncoder(Fixed32ByteSizeEncoder):
253
271
  if self.type_check_fn is None:
254
272
  raise ValueError("`type_check_fn` cannot be null")
255
273
 
256
- def validate_value(self, value):
257
- if self.type_check_fn is None:
274
+ def validate_value(self, value: Any) -> None:
275
+ type_check_fn = self.type_check_fn
276
+ if type_check_fn is None:
258
277
  raise AssertionError("`type_check_fn` is None")
259
- if not self.type_check_fn(value):
278
+ if not type_check_fn(value):
260
279
  self.invalidate_value(value)
261
280
 
262
- illegal_value = self.illegal_value_fn is not None and self.illegal_value_fn(
263
- value
264
- )
281
+ illegal_value_fn = self.illegal_value_fn
282
+ illegal_value = illegal_value_fn is not None and illegal_value_fn(value)
265
283
  if illegal_value:
266
284
  self.invalidate_value(value, exc=IllegalValue)
267
285
 
@@ -285,6 +303,16 @@ class UnsignedIntegerEncoder(NumberEncoder):
285
303
  return cls(value_bit_size=abi_type.sub)
286
304
 
287
305
 
306
+ class UnsignedIntegerEncoderCached(UnsignedIntegerEncoder):
307
+ encode: Final[Callable[[int], bytes]]
308
+ maxsize: Final[Optional[int]]
309
+
310
+ def __init__(self, maxsize: Optional[int] = None, **kwargs: Any) -> None:
311
+ super().__init__(**kwargs)
312
+ self.maxsize = maxsize
313
+ self.encode = lru_cache(maxsize=maxsize)(self.encode)
314
+
315
+
288
316
  encode_uint_256 = UnsignedIntegerEncoder(value_bit_size=256, data_byte_size=32)
289
317
 
290
318
 
@@ -297,29 +325,44 @@ class PackedUnsignedIntegerEncoder(UnsignedIntegerEncoder):
297
325
  )
298
326
 
299
327
 
328
+ class PackedUnsignedIntegerEncoderCached(PackedUnsignedIntegerEncoder):
329
+ encode: Final[Callable[[int], bytes]]
330
+ maxsize: Final[Optional[int]]
331
+
332
+ def __init__(self, maxsize: Optional[int] = None, **kwargs: Any) -> None:
333
+ super().__init__(**kwargs)
334
+ self.maxsize = maxsize
335
+ self.encode = lru_cache(maxsize=maxsize)(self.encode)
336
+
337
+
300
338
  class SignedIntegerEncoder(NumberEncoder):
301
339
  bounds_fn = staticmethod(compute_signed_integer_bounds)
302
340
  type_check_fn = staticmethod(is_integer)
303
341
 
304
- def encode_fn(self, value):
342
+ def encode_fn(self, value: int) -> bytes:
305
343
  return int_to_big_endian(value % (2**self.value_bit_size))
306
344
 
307
- def encode(self, value):
345
+ def encode(self, value: int) -> bytes:
308
346
  self.validate_value(value)
309
- base_encoded_value = self.encode_fn(value)
310
-
311
- if value >= 0:
312
- padded_encoded_value = zpad(base_encoded_value, self.data_byte_size)
313
- else:
314
- padded_encoded_value = fpad(base_encoded_value, self.data_byte_size)
347
+ return encode_signed(value, self.encode_fn, self.data_byte_size)
315
348
 
316
- return padded_encoded_value
349
+ __call__ = encode
317
350
 
318
351
  @parse_type_str("int")
319
352
  def from_type_str(cls, abi_type, registry):
320
353
  return cls(value_bit_size=abi_type.sub)
321
354
 
322
355
 
356
+ class SignedIntegerEncoderCached(SignedIntegerEncoder):
357
+ encode: Final[Callable[[int], bytes]]
358
+ maxsize: Final[Optional[int]]
359
+
360
+ def __init__(self, maxsize: Optional[int] = None, **kwargs: Any) -> None:
361
+ super().__init__(**kwargs)
362
+ self.maxsize = maxsize
363
+ self.encode = lru_cache(maxsize=maxsize)(self.encode)
364
+
365
+
323
366
  class PackedSignedIntegerEncoder(SignedIntegerEncoder):
324
367
  @parse_type_str("int")
325
368
  def from_type_str(cls, abi_type, registry):
@@ -329,6 +372,16 @@ class PackedSignedIntegerEncoder(SignedIntegerEncoder):
329
372
  )
330
373
 
331
374
 
375
+ class PackedSignedIntegerEncoderCached(PackedSignedIntegerEncoder):
376
+ encode: Final[Callable[[int], bytes]]
377
+ maxsize: Final[Optional[int]]
378
+
379
+ def __init__(self, maxsize: Optional[int] = None, **kwargs: Any) -> None:
380
+ super().__init__(**kwargs)
381
+ self.maxsize = maxsize
382
+ self.encode = lru_cache(maxsize=maxsize)(self.encode)
383
+
384
+
332
385
  class BaseFixedEncoder(NumberEncoder):
333
386
  frac_places = None
334
387
 
@@ -343,27 +396,36 @@ class BaseFixedEncoder(NumberEncoder):
343
396
 
344
397
  return False
345
398
 
399
+ @cached_property
400
+ def denominator(self) -> decimal.Decimal:
401
+ return TEN**self.frac_places
402
+
403
+ @cached_property
404
+ def precision(self) -> int:
405
+ return TEN**-self.frac_places
406
+
346
407
  def validate_value(self, value):
347
408
  super().validate_value(value)
348
409
 
349
410
  with decimal.localcontext(abi_decimal_context):
350
- residue = value % (TEN**-self.frac_places)
411
+ residue = value % self.precision
351
412
 
352
413
  if residue > 0:
353
414
  self.invalidate_value(
354
415
  value,
355
416
  exc=IllegalValue,
356
- msg=f"residue {repr(residue)} outside allowed fractional precision of "
417
+ msg=f"residue {residue!r} outside allowed fractional precision of "
357
418
  f"{self.frac_places}",
358
419
  )
359
420
 
360
- def validate(self):
421
+ def validate(self) -> None:
361
422
  super().validate()
362
423
 
363
- if self.frac_places is None:
424
+ frac_places = self.frac_places
425
+ if frac_places is None:
364
426
  raise ValueError("must specify `frac_places`")
365
427
 
366
- if self.frac_places <= 0 or self.frac_places > 80:
428
+ if frac_places <= 0 or frac_places > 80:
367
429
  raise ValueError("`frac_places` must be in range (0, 80]")
368
430
 
369
431
 
@@ -371,9 +433,9 @@ class UnsignedFixedEncoder(BaseFixedEncoder):
371
433
  def bounds_fn(self, value_bit_size):
372
434
  return compute_unsigned_fixed_bounds(self.value_bit_size, self.frac_places)
373
435
 
374
- def encode_fn(self, value):
436
+ def encode_fn(self, value: decimal.Decimal) -> bytes:
375
437
  with decimal.localcontext(abi_decimal_context):
376
- scaled_value = value * TEN**self.frac_places
438
+ scaled_value = value * self.denominator
377
439
  integer_value = int(scaled_value)
378
440
 
379
441
  return int_to_big_endian(integer_value)
@@ -404,25 +466,20 @@ class SignedFixedEncoder(BaseFixedEncoder):
404
466
  def bounds_fn(self, value_bit_size):
405
467
  return compute_signed_fixed_bounds(self.value_bit_size, self.frac_places)
406
468
 
407
- def encode_fn(self, value):
469
+ def encode_fn(self, value: decimal.Decimal) -> bytes:
408
470
  with decimal.localcontext(abi_decimal_context):
409
- scaled_value = value * TEN**self.frac_places
471
+ scaled_value = value * self.denominator
410
472
  integer_value = int(scaled_value)
411
473
 
412
474
  unsigned_integer_value = integer_value % (2**self.value_bit_size)
413
475
 
414
476
  return int_to_big_endian(unsigned_integer_value)
415
477
 
416
- def encode(self, value):
478
+ def encode(self, value: decimal.Decimal) -> bytes:
417
479
  self.validate_value(value)
418
- base_encoded_value = self.encode_fn(value)
480
+ return encode_signed(value, self.encode_fn, self.data_byte_size)
419
481
 
420
- if value >= 0:
421
- padded_encoded_value = zpad(base_encoded_value, self.data_byte_size)
422
- else:
423
- padded_encoded_value = fpad(base_encoded_value, self.data_byte_size)
424
-
425
- return padded_encoded_value
482
+ __call__ = encode
426
483
 
427
484
  @parse_type_str("fixed")
428
485
  def from_type_str(cls, abi_type, registry):
@@ -452,11 +509,11 @@ class AddressEncoder(Fixed32ByteSizeEncoder):
452
509
  is_big_endian = True
453
510
 
454
511
  @classmethod
455
- def validate_value(cls, value):
512
+ def validate_value(cls, value: Any) -> None:
456
513
  if not is_address(value):
457
514
  cls.invalidate_value(value)
458
515
 
459
- def validate(self):
516
+ def validate(self) -> None:
460
517
  super().validate()
461
518
 
462
519
  if self.value_bit_size != 20 * 8:
@@ -474,7 +531,7 @@ class PackedAddressEncoder(AddressEncoder):
474
531
  class BytesEncoder(Fixed32ByteSizeEncoder):
475
532
  is_big_endian = False
476
533
 
477
- def validate_value(self, value):
534
+ def validate_value(self, value: Any) -> None:
478
535
  if not is_bytes(value):
479
536
  self.invalidate_value(value)
480
537
 
@@ -487,7 +544,7 @@ class BytesEncoder(Fixed32ByteSizeEncoder):
487
544
  )
488
545
 
489
546
  @staticmethod
490
- def encode_fn(value):
547
+ def encode_fn(value: bytes) -> bytes:
491
548
  return value
492
549
 
493
550
  @parse_type_str("bytes")
@@ -508,12 +565,12 @@ class ByteStringEncoder(BaseEncoder):
508
565
  is_dynamic = True
509
566
 
510
567
  @classmethod
511
- def validate_value(cls, value):
568
+ def validate_value(cls, value: Any) -> None:
512
569
  if not is_bytes(value):
513
570
  cls.invalidate_value(value)
514
571
 
515
572
  @classmethod
516
- def encode(cls, value):
573
+ def encode(cls, value: bytes) -> bytes:
517
574
  cls.validate_value(value)
518
575
  value_length = len(value)
519
576
 
@@ -522,30 +579,34 @@ class ByteStringEncoder(BaseEncoder):
522
579
 
523
580
  return encoded_size + padded_value
524
581
 
582
+ __call__: ClassVar[Callable[[Type[Self], bytes], bytes]] = encode
583
+
525
584
  @parse_type_str("bytes")
526
585
  def from_type_str(cls, abi_type, registry):
527
- return cls()
586
+ return cls() # type: ignore [misc]
528
587
 
529
588
 
530
589
  class PackedByteStringEncoder(ByteStringEncoder):
531
590
  is_dynamic = False
532
591
 
533
592
  @classmethod
534
- def encode(cls, value):
593
+ def encode(cls, value: bytes) -> bytes:
535
594
  cls.validate_value(value)
536
595
  return value
537
596
 
597
+ __call__ = encode
598
+
538
599
 
539
600
  class TextStringEncoder(BaseEncoder):
540
601
  is_dynamic = True
541
602
 
542
603
  @classmethod
543
- def validate_value(cls, value):
604
+ def validate_value(cls, value: Any) -> None:
544
605
  if not is_text(value):
545
606
  cls.invalidate_value(value)
546
607
 
547
608
  @classmethod
548
- def encode(cls, value):
609
+ def encode(cls, value: str) -> bytes:
549
610
  cls.validate_value(value)
550
611
 
551
612
  value_as_bytes = codecs.encode(value, "utf8")
@@ -556,57 +617,47 @@ class TextStringEncoder(BaseEncoder):
556
617
 
557
618
  return encoded_size + padded_value
558
619
 
620
+ __call__: ClassVar[Callable[[Type[Self], str], bytes]] = encode
621
+
559
622
  @parse_type_str("string")
560
623
  def from_type_str(cls, abi_type, registry):
561
- return cls()
624
+ return cls() # type: ignore [misc]
562
625
 
563
626
 
564
627
  class PackedTextStringEncoder(TextStringEncoder):
565
628
  is_dynamic = False
566
629
 
567
630
  @classmethod
568
- def encode(cls, value):
631
+ def encode(cls, value: str) -> bytes:
569
632
  cls.validate_value(value)
570
633
  return codecs.encode(value, "utf8")
571
634
 
635
+ __call__ = encode
636
+
572
637
 
573
638
  class BaseArrayEncoder(BaseEncoder):
574
- item_encoder = None
639
+ item_encoder: BaseEncoder = None
575
640
 
576
- def validate(self):
641
+ def validate(self) -> None:
577
642
  super().validate()
578
643
 
579
644
  if self.item_encoder is None:
580
645
  raise ValueError("`item_encoder` may not be none")
581
646
 
582
- def validate_value(self, value):
647
+ def validate_value(self, value: Any) -> None:
583
648
  if not is_list_like(value):
584
649
  self.invalidate_value(
585
650
  value,
586
651
  msg="must be list-like such as array or tuple",
587
652
  )
588
653
 
654
+ item_encoder = self.item_encoder
589
655
  for item in value:
590
- self.item_encoder.validate_value(item)
656
+ item_encoder.validate_value(item)
591
657
 
592
- def encode_elements(self, value):
658
+ def encode_elements(self, value: Sequence[Any]) -> bytes:
593
659
  self.validate_value(value)
594
-
595
- item_encoder = self.item_encoder
596
- if item_encoder is None:
597
- raise AssertionError("`item_encoder` is None")
598
- tail_chunks = tuple(item_encoder(i) for i in value)
599
-
600
- items_are_dynamic = getattr(item_encoder, "is_dynamic", False)
601
- if not items_are_dynamic or len(value) == 0:
602
- return b"".join(tail_chunks)
603
-
604
- head_length = 32 * len(value)
605
- tail_offsets = (0,) + tuple(accumulate(map(len, tail_chunks[:-1])))
606
- head_chunks = tuple(
607
- encode_uint_256(head_length + offset) for offset in tail_offsets
608
- )
609
- return b"".join(head_chunks + tail_chunks)
660
+ return encode_elements(self.item_encoder, value)
610
661
 
611
662
  @parse_type_str(with_arrlist=True)
612
663
  def from_type_str(cls, abi_type, registry):
@@ -627,21 +678,21 @@ class BaseArrayEncoder(BaseEncoder):
627
678
  class PackedArrayEncoder(BaseArrayEncoder):
628
679
  array_size = None
629
680
 
630
- def validate_value(self, value):
681
+ def validate_value(self, value: Any) -> None:
631
682
  super().validate_value(value)
632
683
 
633
- if self.array_size is not None and len(value) != self.array_size:
684
+ array_size = self.array_size
685
+ if array_size is not None and len(value) != array_size:
634
686
  self.invalidate_value(
635
687
  value,
636
688
  exc=ValueOutOfBounds,
637
- msg=f"value has {len(value)} items when {self.array_size} were "
638
- "expected",
689
+ msg=f"value has {len(value)} items when {array_size} were expected",
639
690
  )
640
691
 
641
- def encode(self, value):
642
- encoded_elements = self.encode_elements(value)
692
+ def encode(self, value: Sequence[Any]) -> bytes:
693
+ return encode_elements(self.item_encoder, value)
643
694
 
644
- return encoded_elements
695
+ __call__ = encode
645
696
 
646
697
  @parse_type_str(with_arrlist=True)
647
698
  def from_type_str(cls, abi_type, registry):
@@ -660,18 +711,18 @@ class PackedArrayEncoder(BaseArrayEncoder):
660
711
  class SizedArrayEncoder(BaseArrayEncoder):
661
712
  array_size = None
662
713
 
663
- def __init__(self, **kwargs):
714
+ def __init__(self, **kwargs: Any) -> None:
664
715
  super().__init__(**kwargs)
665
716
 
666
717
  self.is_dynamic = self.item_encoder.is_dynamic
667
718
 
668
- def validate(self):
719
+ def validate(self) -> None:
669
720
  super().validate()
670
721
 
671
722
  if self.array_size is None:
672
723
  raise ValueError("`array_size` may not be none")
673
724
 
674
- def validate_value(self, value):
725
+ def validate_value(self, value: Any) -> None:
675
726
  super().validate_value(value)
676
727
 
677
728
  if len(value) != self.array_size:
@@ -682,18 +733,14 @@ class SizedArrayEncoder(BaseArrayEncoder):
682
733
  "expected",
683
734
  )
684
735
 
685
- def encode(self, value):
686
- encoded_elements = self.encode_elements(value)
736
+ def encode(self, value: Sequence[Any]) -> bytes:
737
+ return encode_elements(self.item_encoder, value)
687
738
 
688
- return encoded_elements
739
+ __call__ = encode
689
740
 
690
741
 
691
742
  class DynamicArrayEncoder(BaseArrayEncoder):
692
743
  is_dynamic = True
693
744
 
694
- def encode(self, value):
695
- encoded_size = encode_uint_256(len(value))
696
- encoded_elements = self.encode_elements(value)
697
- encoded_value = encoded_size + encoded_elements
698
-
699
- return encoded_value
745
+ def encode(self, value: Sequence[Any]) -> bytes:
746
+ return encode_elements_dynamic(self.item_encoder, value)