faster-eth-abi 5.2.22__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.
Files changed (46) hide show
  1. faster_eth_abi/__init__.py +12 -0
  2. faster_eth_abi/_codec.cpython-313-darwin.so +0 -0
  3. faster_eth_abi/_codec.py +83 -0
  4. faster_eth_abi/_decoding.cpython-313-darwin.so +0 -0
  5. faster_eth_abi/_decoding.py +299 -0
  6. faster_eth_abi/_encoding.cpython-313-darwin.so +0 -0
  7. faster_eth_abi/_encoding.py +354 -0
  8. faster_eth_abi/_grammar.cpython-313-darwin.so +0 -0
  9. faster_eth_abi/_grammar.py +375 -0
  10. faster_eth_abi/abi.cpython-313-darwin.so +0 -0
  11. faster_eth_abi/abi.py +17 -0
  12. faster_eth_abi/base.py +45 -0
  13. faster_eth_abi/codec.py +2809 -0
  14. faster_eth_abi/constants.cpython-313-darwin.so +0 -0
  15. faster_eth_abi/constants.py +7 -0
  16. faster_eth_abi/decoding.py +584 -0
  17. faster_eth_abi/encoding.py +746 -0
  18. faster_eth_abi/exceptions.py +127 -0
  19. faster_eth_abi/from_type_str.cpython-313-darwin.so +0 -0
  20. faster_eth_abi/from_type_str.py +141 -0
  21. faster_eth_abi/grammar.py +172 -0
  22. faster_eth_abi/io.py +107 -0
  23. faster_eth_abi/packed.cpython-313-darwin.so +0 -0
  24. faster_eth_abi/packed.py +19 -0
  25. faster_eth_abi/py.typed +0 -0
  26. faster_eth_abi/registry.py +758 -0
  27. faster_eth_abi/tools/__init__.cpython-313-darwin.so +0 -0
  28. faster_eth_abi/tools/__init__.py +3 -0
  29. faster_eth_abi/tools/_strategies.cpython-313-darwin.so +0 -0
  30. faster_eth_abi/tools/_strategies.py +243 -0
  31. faster_eth_abi/typing.py +4627 -0
  32. faster_eth_abi/utils/__init__.cpython-313-darwin.so +0 -0
  33. faster_eth_abi/utils/__init__.py +0 -0
  34. faster_eth_abi/utils/numeric.cpython-313-darwin.so +0 -0
  35. faster_eth_abi/utils/numeric.py +117 -0
  36. faster_eth_abi/utils/padding.cpython-313-darwin.so +0 -0
  37. faster_eth_abi/utils/padding.py +22 -0
  38. faster_eth_abi/utils/string.cpython-313-darwin.so +0 -0
  39. faster_eth_abi/utils/string.py +19 -0
  40. faster_eth_abi/utils/validation.cpython-313-darwin.so +0 -0
  41. faster_eth_abi/utils/validation.py +18 -0
  42. faster_eth_abi-5.2.22.dist-info/METADATA +136 -0
  43. faster_eth_abi-5.2.22.dist-info/RECORD +46 -0
  44. faster_eth_abi-5.2.22.dist-info/WHEEL +6 -0
  45. faster_eth_abi-5.2.22.dist-info/top_level.txt +2 -0
  46. faster_eth_abi__mypyc.cpython-313-darwin.so +0 -0
@@ -0,0 +1,7 @@
1
+ from typing import (
2
+ Final,
3
+ )
4
+
5
+ TT256: Final = 2**256
6
+ TT256M1: Final = 2**256 - 1
7
+ TT255: Final = 2**255
@@ -0,0 +1,584 @@
1
+ """Classes for ABI decoding logic.
2
+
3
+ Implements classes and functions for deserializing binary data into Python values
4
+ according to ABI type specifications.
5
+ """
6
+ import abc
7
+ import decimal
8
+ from functools import (
9
+ cached_property,
10
+ lru_cache,
11
+ )
12
+ from types import (
13
+ MethodType,
14
+ )
15
+ from typing import (
16
+ Any,
17
+ Callable,
18
+ Final,
19
+ Generic,
20
+ Optional,
21
+ Tuple,
22
+ TypeVar,
23
+ Union,
24
+ final,
25
+ )
26
+
27
+ from eth_typing import (
28
+ HexAddress,
29
+ )
30
+ from faster_eth_utils import (
31
+ big_endian_to_int,
32
+ to_normalized_address,
33
+ )
34
+
35
+ from faster_eth_abi._decoding import (
36
+ decode_dynamic_array,
37
+ decode_head_tail,
38
+ decode_sized_array,
39
+ decode_tuple,
40
+ decoder_fn_boolean,
41
+ get_value_byte_size,
42
+ read_fixed_byte_size_data_from_stream,
43
+ split_data_and_padding_fixed_byte_size,
44
+ validate_padding_bytes_fixed_byte_size,
45
+ validate_padding_bytes_signed_integer,
46
+ validate_pointers_array,
47
+ )
48
+ from faster_eth_abi.base import (
49
+ BaseCoder,
50
+ )
51
+ from faster_eth_abi.exceptions import (
52
+ InsufficientDataBytes,
53
+ NonEmptyPaddingBytes,
54
+ )
55
+ from faster_eth_abi.from_type_str import (
56
+ parse_tuple_type_str,
57
+ parse_type_str,
58
+ )
59
+ from faster_eth_abi.io import (
60
+ ContextFramesBytesIO,
61
+ )
62
+ from faster_eth_abi.typing import (
63
+ T,
64
+ )
65
+ from faster_eth_abi.utils.numeric import (
66
+ TEN,
67
+ abi_decimal_context,
68
+ ceil32,
69
+ )
70
+
71
+ TByteStr = TypeVar("TByteStr", bytes, str)
72
+
73
+
74
+ class BaseDecoder(BaseCoder, Generic[T], metaclass=abc.ABCMeta):
75
+ """
76
+ Base class for all decoder classes. Subclass this if you want to define a
77
+ custom decoder class. Subclasses must also implement
78
+ :any:`BaseCoder.from_type_str`.
79
+ """
80
+
81
+ strict = True
82
+
83
+ @abc.abstractmethod
84
+ def decode(self, stream: ContextFramesBytesIO) -> T: # pragma: no cover
85
+ """
86
+ Decodes the given stream of bytes into a python value. Should raise
87
+ :any:`exceptions.DecodingError` if a python value cannot be decoded
88
+ from the given byte stream.
89
+ """
90
+
91
+ def __call__(self, stream: ContextFramesBytesIO) -> T:
92
+ return self.decode(stream)
93
+
94
+
95
+ class HeadTailDecoder(BaseDecoder[T]):
96
+ """
97
+ Decoder for a dynamic element of a dynamic container (a dynamic array, or a sized
98
+ array or tuple that contains dynamic elements). A dynamic element consists of a
99
+ pointer, aka offset, which is located in the head section of the encoded container,
100
+ and the actual value, which is located in the tail section of the encoding.
101
+ """
102
+
103
+ is_dynamic = True
104
+
105
+ def __init__(
106
+ self,
107
+ tail_decoder: Union[ # type: ignore [type-var]
108
+ "HeadTailDecoder[T]",
109
+ "SizedArrayDecoder[T]",
110
+ "DynamicArrayDecoder[T]",
111
+ "ByteStringDecoder[T]",
112
+ ],
113
+ ) -> None:
114
+ super().__init__()
115
+
116
+ if tail_decoder is None:
117
+ raise ValueError("No `tail_decoder` set")
118
+
119
+ self.tail_decoder: Final = tail_decoder
120
+
121
+ def decode(self, stream: ContextFramesBytesIO) -> T:
122
+ return decode_head_tail(self, stream)
123
+
124
+ __call__ = decode
125
+
126
+
127
+ class TupleDecoder(BaseDecoder[Tuple[T, ...]]):
128
+ decoders: Tuple[BaseDecoder[T], ...] = ()
129
+
130
+ def __init__(self, decoders: Tuple[BaseDecoder[T], ...]) -> None:
131
+ super().__init__()
132
+
133
+ self.decoders = decoders = tuple(
134
+ HeadTailDecoder(tail_decoder=d) if getattr(d, "is_dynamic", False) else d
135
+ for d in decoders
136
+ )
137
+
138
+ self.is_dynamic = any(getattr(d, "is_dynamic", False) for d in decoders)
139
+ self.len_of_head = sum(
140
+ getattr(decoder, "array_size", 1) for decoder in decoders
141
+ )
142
+ self._is_head_tail = tuple(
143
+ isinstance(decoder, HeadTailDecoder) for decoder in decoders
144
+ )
145
+ self._no_head_tail = not any(self._is_head_tail)
146
+
147
+ def validate(self) -> None:
148
+ super().validate()
149
+
150
+ if self.decoders is None:
151
+ raise ValueError("No `decoders` set")
152
+
153
+ @final
154
+ def validate_pointers(self, stream: ContextFramesBytesIO) -> None:
155
+ raise NotImplementedError("didnt call __init__")
156
+
157
+ def decode(self, stream: ContextFramesBytesIO) -> Tuple[T, ...]:
158
+ return decode_tuple(self, stream)
159
+
160
+ __call__ = decode
161
+
162
+ @parse_tuple_type_str
163
+ def from_type_str(cls, abi_type, registry):
164
+ decoders = tuple(
165
+ registry.get_decoder(c.to_type_str()) for c in abi_type.components
166
+ )
167
+
168
+ return cls(decoders=decoders)
169
+
170
+
171
+ class SingleDecoder(BaseDecoder[T]):
172
+ decoder_fn = None
173
+
174
+ def validate(self) -> None:
175
+ super().validate()
176
+
177
+ if self.decoder_fn is None:
178
+ raise ValueError("No `decoder_fn` set")
179
+
180
+ def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
181
+ raise NotImplementedError("Must be implemented by subclasses")
182
+
183
+ def decode(self, stream: ContextFramesBytesIO) -> T:
184
+ raw_data = self.read_data_from_stream(stream)
185
+ data, padding_bytes = self.split_data_and_padding(raw_data)
186
+ value = self.decoder_fn(data) # type: ignore [misc]
187
+ self.validate_padding_bytes(value, padding_bytes)
188
+
189
+ return value
190
+
191
+ __call__ = decode
192
+
193
+ def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
194
+ raise NotImplementedError("Must be implemented by subclasses")
195
+
196
+ def split_data_and_padding(self, raw_data: bytes) -> Tuple[bytes, bytes]:
197
+ return raw_data, b""
198
+
199
+
200
+ class BaseArrayDecoder(BaseDecoder[Tuple[T, ...]]):
201
+ item_decoder: BaseDecoder = None
202
+
203
+ def __init__(self, **kwargs: Any) -> None:
204
+ super().__init__(**kwargs)
205
+
206
+ # Use a head-tail decoder to decode dynamic elements
207
+ item_decoder = self.item_decoder
208
+ if item_decoder.is_dynamic:
209
+ self.item_decoder = HeadTailDecoder(tail_decoder=item_decoder)
210
+ self.validate_pointers = MethodType(validate_pointers_array, self)
211
+ else:
212
+
213
+ def noop(stream: ContextFramesBytesIO, array_size: int) -> None:
214
+ ...
215
+
216
+ self.validate_pointers = noop
217
+
218
+ def decode(self, stream: ContextFramesBytesIO) -> Tuple[T, ...]:
219
+ raise NotImplementedError # this is a type stub
220
+
221
+ def validate(self) -> None:
222
+ super().validate()
223
+
224
+ if self.item_decoder is None:
225
+ raise ValueError("No `item_decoder` set")
226
+
227
+ @parse_type_str(with_arrlist=True)
228
+ def from_type_str(cls, abi_type, registry):
229
+ item_decoder = registry.get_decoder(abi_type.item_type.to_type_str())
230
+
231
+ array_spec = abi_type.arrlist[-1]
232
+ if len(array_spec) == 1:
233
+ # If array dimension is fixed
234
+ return SizedArrayDecoder(
235
+ array_size=array_spec[0],
236
+ item_decoder=item_decoder,
237
+ )
238
+ else:
239
+ # If array dimension is dynamic
240
+ return DynamicArrayDecoder(item_decoder=item_decoder)
241
+
242
+ def validate_pointers(self, stream: ContextFramesBytesIO, array_size: int) -> None:
243
+ """
244
+ Verify that all pointers point to a valid location in the stream.
245
+ """
246
+ validate_pointers_array(self, stream, array_size)
247
+
248
+
249
+ class SizedArrayDecoder(BaseArrayDecoder[T]):
250
+ array_size: int = None
251
+
252
+ def __init__(self, **kwargs: Any) -> None:
253
+ super().__init__(**kwargs)
254
+
255
+ self.is_dynamic = self.item_decoder.is_dynamic
256
+
257
+ def decode(self, stream: ContextFramesBytesIO) -> Tuple[T, ...]:
258
+ return decode_sized_array(self, stream)
259
+
260
+ __call__ = decode
261
+
262
+
263
+ class DynamicArrayDecoder(BaseArrayDecoder[T]):
264
+ # Dynamic arrays are always dynamic, regardless of their elements
265
+ is_dynamic = True
266
+
267
+ def decode(self, stream: ContextFramesBytesIO) -> Tuple[T, ...]:
268
+ return decode_dynamic_array(self, stream)
269
+
270
+ __call__ = decode
271
+
272
+
273
+ class FixedByteSizeDecoder(SingleDecoder[T]):
274
+ decoder_fn: Callable[[bytes], T] = None
275
+ value_bit_size: int = None
276
+ data_byte_size: int = None
277
+ is_big_endian: bool = None
278
+
279
+ def __init__(self, **kwargs: Any) -> None:
280
+ super().__init__(**kwargs)
281
+
282
+ self.read_data_from_stream = MethodType(
283
+ read_fixed_byte_size_data_from_stream, self
284
+ )
285
+ self.split_data_and_padding = MethodType(
286
+ split_data_and_padding_fixed_byte_size, self
287
+ )
288
+ self._get_value_byte_size = MethodType(get_value_byte_size, self)
289
+
290
+ # Only assign validate_padding_bytes if not overridden in subclass
291
+ if type(self).validate_padding_bytes is SingleDecoder.validate_padding_bytes:
292
+ self.validate_padding_bytes = MethodType(
293
+ validate_padding_bytes_fixed_byte_size, self
294
+ )
295
+
296
+ def validate(self) -> None:
297
+ super().validate()
298
+
299
+ value_bit_size = self.value_bit_size
300
+ if value_bit_size is None:
301
+ raise ValueError("`value_bit_size` may not be None")
302
+ data_byte_size = self.data_byte_size
303
+ if data_byte_size is None:
304
+ raise ValueError("`data_byte_size` may not be None")
305
+ if self.decoder_fn is None:
306
+ raise ValueError("`decoder_fn` may not be None")
307
+ if self.is_big_endian is None:
308
+ raise ValueError("`is_big_endian` may not be None")
309
+
310
+ if value_bit_size % 8 != 0:
311
+ raise ValueError(
312
+ f"Invalid value bit size: {value_bit_size}. Must be a multiple of 8"
313
+ )
314
+
315
+ if value_bit_size > data_byte_size * 8:
316
+ raise ValueError("Value byte size exceeds data size")
317
+
318
+ def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
319
+ raise NotImplementedError("didnt call __init__")
320
+
321
+ def split_data_and_padding(self, raw_data: bytes) -> Tuple[bytes, bytes]:
322
+ raise NotImplementedError("didnt call __init__")
323
+
324
+ # This is unused, but it is kept in to preserve the eth-abi api
325
+ def _get_value_byte_size(self) -> int:
326
+ raise NotImplementedError("didnt call __init__")
327
+
328
+
329
+ class Fixed32ByteSizeDecoder(FixedByteSizeDecoder[T]):
330
+ data_byte_size = 32
331
+
332
+
333
+ class BooleanDecoder(Fixed32ByteSizeDecoder[bool]):
334
+ value_bit_size = 8
335
+ is_big_endian = True
336
+
337
+ decoder_fn = staticmethod(decoder_fn_boolean)
338
+
339
+ @parse_type_str("bool")
340
+ def from_type_str(cls, abi_type, registry):
341
+ return cls()
342
+
343
+
344
+ class AddressDecoder(Fixed32ByteSizeDecoder[HexAddress]):
345
+ value_bit_size = 20 * 8
346
+ is_big_endian = True
347
+ decoder_fn = staticmethod(to_normalized_address)
348
+
349
+ @parse_type_str("address")
350
+ def from_type_str(cls, abi_type, registry):
351
+ return cls()
352
+
353
+
354
+ #
355
+ # Unsigned Integer Decoders
356
+ #
357
+ class UnsignedIntegerDecoder(Fixed32ByteSizeDecoder[int]):
358
+ decoder_fn: "staticmethod[[bytes], int]" = staticmethod(big_endian_to_int)
359
+ is_big_endian = True
360
+
361
+ @parse_type_str("uint")
362
+ def from_type_str(cls, abi_type, registry):
363
+ return cls(value_bit_size=abi_type.sub)
364
+
365
+
366
+ decode_uint_256 = UnsignedIntegerDecoder(value_bit_size=256)
367
+
368
+
369
+ class UnsignedIntegerDecoderCached(UnsignedIntegerDecoder):
370
+ decoder_fn: Callable[[bytes], int]
371
+ maxsize: Final[Optional[int]]
372
+
373
+ def __init__(self, maxsize: Optional[int] = None, **kwargs: Any) -> None:
374
+ super().__init__(**kwargs)
375
+ self.maxsize = maxsize
376
+ self.decoder_fn = lru_cache(maxsize=maxsize)(self.decoder_fn)
377
+
378
+
379
+ #
380
+ # Signed Integer Decoders
381
+ #
382
+ class SignedIntegerDecoder(Fixed32ByteSizeDecoder[int]):
383
+ is_big_endian = True
384
+
385
+ def __init__(self, **kwargs: Any) -> None:
386
+ super().__init__(**kwargs)
387
+
388
+ # Only assign validate_padding_bytes if not overridden in subclass
389
+ if (
390
+ type(self).validate_padding_bytes
391
+ is SignedIntegerDecoder.validate_padding_bytes
392
+ ):
393
+ self.validate_padding_bytes = MethodType(
394
+ validate_padding_bytes_signed_integer, self
395
+ )
396
+
397
+ @cached_property
398
+ def neg_threshold(self) -> int:
399
+ return int(2 ** (self.value_bit_size - 1))
400
+
401
+ @cached_property
402
+ def neg_offset(self) -> int:
403
+ return int(2**self.value_bit_size)
404
+
405
+ def decoder_fn(self, data: bytes) -> int:
406
+ value = big_endian_to_int(data)
407
+ if value >= self.neg_threshold:
408
+ value -= self.neg_offset
409
+ return value
410
+
411
+ def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
412
+ return validate_padding_bytes_signed_integer(self, value, padding_bytes)
413
+
414
+ @parse_type_str("int")
415
+ def from_type_str(cls, abi_type, registry):
416
+ return cls(value_bit_size=abi_type.sub)
417
+
418
+
419
+ class SignedIntegerDecoderCached(SignedIntegerDecoder):
420
+ decoder_fn: Callable[[bytes], int]
421
+ maxsize: Final[Optional[int]]
422
+
423
+ def __init__(self, maxsize: Optional[int] = None, **kwargs: Any) -> None:
424
+ super().__init__(**kwargs)
425
+ self.maxsize = maxsize
426
+ self.decoder_fn = lru_cache(maxsize=maxsize)(self.decoder_fn)
427
+
428
+
429
+ #
430
+ # Bytes1..32
431
+ #
432
+ class BytesDecoder(Fixed32ByteSizeDecoder[bytes]):
433
+ is_big_endian = False
434
+
435
+ @staticmethod
436
+ def decoder_fn(data: bytes) -> bytes:
437
+ return data
438
+
439
+ @parse_type_str("bytes")
440
+ def from_type_str(cls, abi_type, registry):
441
+ return cls(value_bit_size=abi_type.sub * 8)
442
+
443
+
444
+ class BaseFixedDecoder(Fixed32ByteSizeDecoder[decimal.Decimal]):
445
+ frac_places: int = None
446
+ is_big_endian = True
447
+
448
+ @cached_property
449
+ def denominator(self) -> decimal.Decimal:
450
+ return TEN**self.frac_places
451
+
452
+ def validate(self) -> None:
453
+ super().validate()
454
+
455
+ frac_places = self.frac_places
456
+ if frac_places is None:
457
+ raise ValueError("must specify `frac_places`")
458
+
459
+ if frac_places <= 0 or frac_places > 80:
460
+ raise ValueError("`frac_places` must be in range (0, 80)")
461
+
462
+
463
+ class UnsignedFixedDecoder(BaseFixedDecoder):
464
+ def decoder_fn(self, data: bytes) -> decimal.Decimal:
465
+ value = big_endian_to_int(data)
466
+
467
+ with decimal.localcontext(abi_decimal_context):
468
+ decimal_value = decimal.Decimal(value) / self.denominator
469
+
470
+ return decimal_value
471
+
472
+ @parse_type_str("ufixed")
473
+ def from_type_str(cls, abi_type, registry):
474
+ value_bit_size, frac_places = abi_type.sub
475
+
476
+ return cls(value_bit_size=value_bit_size, frac_places=frac_places)
477
+
478
+
479
+ class SignedFixedDecoder(BaseFixedDecoder):
480
+ @cached_property
481
+ def neg_threshold(self) -> int:
482
+ return int(2 ** (self.value_bit_size - 1))
483
+
484
+ @cached_property
485
+ def neg_offset(self) -> int:
486
+ return int(2**self.value_bit_size)
487
+
488
+ @cached_property
489
+ def expected_padding_pos(self) -> bytes:
490
+ value_byte_size = get_value_byte_size(self)
491
+ padding_size = self.data_byte_size - value_byte_size
492
+ return b"\x00" * padding_size
493
+
494
+ @cached_property
495
+ def expected_padding_neg(self) -> bytes:
496
+ value_byte_size = get_value_byte_size(self)
497
+ padding_size = self.data_byte_size - value_byte_size
498
+ return b"\xff" * padding_size
499
+
500
+ def decoder_fn(self, data: bytes) -> decimal.Decimal:
501
+ value = big_endian_to_int(data)
502
+ if value >= self.neg_threshold:
503
+ value -= self.neg_offset
504
+
505
+ with decimal.localcontext(abi_decimal_context):
506
+ decimal_value = decimal.Decimal(value) / self.denominator
507
+
508
+ return decimal_value
509
+
510
+ def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
511
+ if value >= 0:
512
+ expected_padding_bytes = self.expected_padding_pos
513
+ else:
514
+ expected_padding_bytes = self.expected_padding_neg
515
+
516
+ if padding_bytes != expected_padding_bytes:
517
+ raise NonEmptyPaddingBytes(
518
+ f"Padding bytes were not empty: {padding_bytes!r}"
519
+ )
520
+
521
+ @parse_type_str("fixed")
522
+ def from_type_str(cls, abi_type, registry):
523
+ value_bit_size, frac_places = abi_type.sub
524
+
525
+ return cls(value_bit_size=value_bit_size, frac_places=frac_places)
526
+
527
+
528
+ #
529
+ # String and Bytes
530
+ #
531
+ class ByteStringDecoder(SingleDecoder[TByteStr]):
532
+ is_dynamic = True
533
+
534
+ @staticmethod
535
+ def decoder_fn(data: bytes) -> bytes:
536
+ return data
537
+
538
+ def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
539
+ data_length = decode_uint_256(stream)
540
+ padded_length = ceil32(data_length)
541
+
542
+ data = stream.read(padded_length)
543
+
544
+ if self.strict:
545
+ if len(data) < padded_length:
546
+ raise InsufficientDataBytes(
547
+ f"Tried to read {padded_length} bytes, only got {len(data)} bytes"
548
+ )
549
+
550
+ padding_bytes = data[data_length:]
551
+ if padding_bytes != b"\x00" * (padded_length - data_length):
552
+ raise NonEmptyPaddingBytes(
553
+ f"Padding bytes were not empty: {padding_bytes!r}"
554
+ )
555
+
556
+ return data[:data_length]
557
+
558
+ def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
559
+ pass
560
+
561
+ @parse_type_str("bytes")
562
+ def from_type_str(cls, abi_type, registry):
563
+ return cls()
564
+
565
+
566
+ class StringDecoder(ByteStringDecoder[str]):
567
+ def __init__(self, handle_string_errors: str = "strict") -> None:
568
+ self.bytes_errors: Final = handle_string_errors
569
+ super().__init__()
570
+
571
+ @parse_type_str("string")
572
+ def from_type_str(cls, abi_type, registry):
573
+ return cls()
574
+
575
+ def decode(self, stream: ContextFramesBytesIO) -> str:
576
+ raw_data = self.read_data_from_stream(stream)
577
+ data, padding_bytes = self.split_data_and_padding(raw_data)
578
+ return self.decoder_fn(data, self.bytes_errors)
579
+
580
+ __call__ = decode
581
+
582
+ @staticmethod
583
+ def decoder_fn(data: bytes, handle_string_errors: str = "strict") -> str:
584
+ return data.decode("utf-8", errors=handle_string_errors)