faster-eth-abi 5.2.12__cp311-cp311-win_amd64.whl → 5.2.14__cp311-cp311-win_amd64.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.
- benchmarks/__init__.py +1 -0
- benchmarks/batch.py +9 -0
- benchmarks/data.py +313 -0
- benchmarks/test_abi_benchmarks.py +82 -0
- benchmarks/test_decoding_benchmarks.py +109 -0
- benchmarks/test_encoding_benchmarks.py +99 -0
- benchmarks/test_grammar_benchmarks.py +38 -0
- benchmarks/test_io_benchmarks.py +99 -0
- benchmarks/test_packed_benchmarks.py +41 -0
- benchmarks/test_registry_benchmarks.py +45 -0
- benchmarks/type_strings.py +26 -0
- faster_eth_abi/_codec.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/_codec.py +1 -1
- faster_eth_abi/_decoding.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/_decoding.py +136 -5
- faster_eth_abi/_encoding.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/_encoding.py +141 -6
- faster_eth_abi/_grammar.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/abi.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/constants.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/decoding.py +107 -96
- faster_eth_abi/encoding.py +55 -27
- faster_eth_abi/from_type_str.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/packed.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/registry.py +47 -31
- faster_eth_abi/tools/__init__.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/tools/_strategies.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/utils/__init__.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/utils/numeric.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/utils/padding.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/utils/string.cp311-win_amd64.pyd +0 -0
- faster_eth_abi/utils/validation.cp311-win_amd64.pyd +0 -0
- {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/METADATA +14 -2
- faster_eth_abi-5.2.14.dist-info/RECORD +57 -0
- {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/top_level.txt +1 -0
- faster_eth_abi__mypyc.cp311-win_amd64.pyd +0 -0
- faster_eth_abi-5.2.12.dist-info/RECORD +0 -46
- {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/WHEEL +0 -0
- {faster_eth_abi-5.2.12.dist-info → faster_eth_abi-5.2.14.dist-info}/licenses/LICENSE +0 -0
faster_eth_abi/decoding.py
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import decimal
|
|
3
|
+
from functools import (
|
|
4
|
+
cached_property,
|
|
5
|
+
)
|
|
6
|
+
from types import (
|
|
7
|
+
MethodType,
|
|
8
|
+
)
|
|
3
9
|
from typing import (
|
|
4
10
|
Any,
|
|
5
11
|
Callable,
|
|
@@ -7,6 +13,7 @@ from typing import (
|
|
|
7
13
|
Optional,
|
|
8
14
|
Tuple,
|
|
9
15
|
Union,
|
|
16
|
+
final,
|
|
10
17
|
)
|
|
11
18
|
|
|
12
19
|
from faster_eth_utils import (
|
|
@@ -24,13 +31,14 @@ from faster_eth_abi._decoding import (
|
|
|
24
31
|
read_fixed_byte_size_data_from_stream,
|
|
25
32
|
split_data_and_padding_fixed_byte_size,
|
|
26
33
|
validate_padding_bytes_fixed_byte_size,
|
|
34
|
+
validate_padding_bytes_signed_integer,
|
|
35
|
+
validate_pointers_array,
|
|
27
36
|
)
|
|
28
37
|
from faster_eth_abi.base import (
|
|
29
38
|
BaseCoder,
|
|
30
39
|
)
|
|
31
40
|
from faster_eth_abi.exceptions import (
|
|
32
41
|
InsufficientDataBytes,
|
|
33
|
-
InvalidPointer,
|
|
34
42
|
NonEmptyPaddingBytes,
|
|
35
43
|
)
|
|
36
44
|
from faster_eth_abi.from_type_str import (
|
|
@@ -111,6 +119,10 @@ class TupleDecoder(BaseDecoder):
|
|
|
111
119
|
self.len_of_head = sum(
|
|
112
120
|
getattr(decoder, "array_size", 1) for decoder in decoders
|
|
113
121
|
)
|
|
122
|
+
self._is_head_tail = tuple(
|
|
123
|
+
isinstance(decoder, HeadTailDecoder) for decoder in decoders
|
|
124
|
+
)
|
|
125
|
+
self._no_head_tail = not any(self._is_head_tail)
|
|
114
126
|
|
|
115
127
|
def validate(self) -> None:
|
|
116
128
|
super().validate()
|
|
@@ -118,35 +130,9 @@ class TupleDecoder(BaseDecoder):
|
|
|
118
130
|
if self.decoders is None:
|
|
119
131
|
raise ValueError("No `decoders` set")
|
|
120
132
|
|
|
133
|
+
@final
|
|
121
134
|
def validate_pointers(self, stream: ContextFramesBytesIO) -> None:
|
|
122
|
-
""
|
|
123
|
-
Verify that all pointers point to a valid location in the stream.
|
|
124
|
-
"""
|
|
125
|
-
current_location = stream.tell()
|
|
126
|
-
end_of_offsets = current_location + 32 * self.len_of_head
|
|
127
|
-
total_stream_length = len(stream.getbuffer())
|
|
128
|
-
for decoder in self.decoders:
|
|
129
|
-
if isinstance(decoder, HeadTailDecoder):
|
|
130
|
-
# the next 32 bytes are a pointer
|
|
131
|
-
offset = decode_uint_256(stream)
|
|
132
|
-
indicated_idx = current_location + offset
|
|
133
|
-
if (
|
|
134
|
-
indicated_idx < end_of_offsets
|
|
135
|
-
or indicated_idx >= total_stream_length
|
|
136
|
-
):
|
|
137
|
-
# the pointer is indicating its data is located either within the
|
|
138
|
-
# offsets section of the stream or beyond the end of the stream,
|
|
139
|
-
# both of which are invalid
|
|
140
|
-
raise InvalidPointer(
|
|
141
|
-
"Invalid pointer in tuple at location "
|
|
142
|
-
f"{stream.tell() - 32} in payload"
|
|
143
|
-
)
|
|
144
|
-
else:
|
|
145
|
-
# the next 32 bytes are not a pointer, so progress the stream per
|
|
146
|
-
# the decoder
|
|
147
|
-
decoder(stream)
|
|
148
|
-
# return the stream to its original location for actual decoding
|
|
149
|
-
stream.seek(current_location)
|
|
135
|
+
raise NotImplementedError("didnt call __init__")
|
|
150
136
|
|
|
151
137
|
def decode(self, stream: ContextFramesBytesIO) -> Tuple[Any, ...]:
|
|
152
138
|
return decode_tuple(self, stream)
|
|
@@ -171,16 +157,13 @@ class SingleDecoder(BaseDecoder):
|
|
|
171
157
|
if self.decoder_fn is None:
|
|
172
158
|
raise ValueError("No `decoder_fn` set")
|
|
173
159
|
|
|
174
|
-
def validate_padding_bytes(self, value, padding_bytes):
|
|
160
|
+
def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
|
|
175
161
|
raise NotImplementedError("Must be implemented by subclasses")
|
|
176
162
|
|
|
177
|
-
def decode(self, stream):
|
|
163
|
+
def decode(self, stream: ContextFramesBytesIO) -> Any:
|
|
178
164
|
raw_data = self.read_data_from_stream(stream)
|
|
179
165
|
data, padding_bytes = self.split_data_and_padding(raw_data)
|
|
180
|
-
|
|
181
|
-
if decoder_fn is None:
|
|
182
|
-
raise AssertionError("`decoder_fn` is None")
|
|
183
|
-
value = decoder_fn(data)
|
|
166
|
+
value = self.decoder_fn(data) # type: ignore [misc]
|
|
184
167
|
self.validate_padding_bytes(value, padding_bytes)
|
|
185
168
|
|
|
186
169
|
return value
|
|
@@ -204,6 +187,16 @@ class BaseArrayDecoder(BaseDecoder):
|
|
|
204
187
|
item_decoder = self.item_decoder
|
|
205
188
|
if item_decoder.is_dynamic:
|
|
206
189
|
self.item_decoder = HeadTailDecoder(tail_decoder=item_decoder)
|
|
190
|
+
self.validate_pointers = MethodType(validate_pointers_array, self)
|
|
191
|
+
else:
|
|
192
|
+
|
|
193
|
+
def noop(stream: ContextFramesBytesIO, array_size: int) -> None:
|
|
194
|
+
...
|
|
195
|
+
|
|
196
|
+
self.validate_pointers = noop
|
|
197
|
+
|
|
198
|
+
def decode(self, stream: ContextFramesBytesIO) -> Tuple[Any, ...]:
|
|
199
|
+
raise NotImplementedError # this is a type stub
|
|
207
200
|
|
|
208
201
|
def validate(self) -> None:
|
|
209
202
|
super().validate()
|
|
@@ -230,25 +223,7 @@ class BaseArrayDecoder(BaseDecoder):
|
|
|
230
223
|
"""
|
|
231
224
|
Verify that all pointers point to a valid location in the stream.
|
|
232
225
|
"""
|
|
233
|
-
|
|
234
|
-
current_location = stream.tell()
|
|
235
|
-
end_of_offsets = current_location + 32 * array_size
|
|
236
|
-
total_stream_length = len(stream.getbuffer())
|
|
237
|
-
for _ in range(array_size):
|
|
238
|
-
offset = decode_uint_256(stream)
|
|
239
|
-
indicated_idx = current_location + offset
|
|
240
|
-
if (
|
|
241
|
-
indicated_idx < end_of_offsets
|
|
242
|
-
or indicated_idx >= total_stream_length
|
|
243
|
-
):
|
|
244
|
-
# the pointer is indicating its data is located either within the
|
|
245
|
-
# offsets section of the stream or beyond the end of the stream,
|
|
246
|
-
# both of which are invalid
|
|
247
|
-
raise InvalidPointer(
|
|
248
|
-
"Invalid pointer in array at location "
|
|
249
|
-
f"{stream.tell() - 32} in payload"
|
|
250
|
-
)
|
|
251
|
-
stream.seek(current_location)
|
|
226
|
+
validate_pointers_array(self, stream, array_size)
|
|
252
227
|
|
|
253
228
|
|
|
254
229
|
class SizedArrayDecoder(BaseArrayDecoder):
|
|
@@ -281,6 +256,23 @@ class FixedByteSizeDecoder(SingleDecoder):
|
|
|
281
256
|
data_byte_size: int = None
|
|
282
257
|
is_big_endian: bool = None
|
|
283
258
|
|
|
259
|
+
def __init__(self, *args, **kwargs):
|
|
260
|
+
super().__init__(*args, **kwargs)
|
|
261
|
+
|
|
262
|
+
self.read_data_from_stream = MethodType(
|
|
263
|
+
read_fixed_byte_size_data_from_stream, self
|
|
264
|
+
)
|
|
265
|
+
self.split_data_and_padding = MethodType(
|
|
266
|
+
split_data_and_padding_fixed_byte_size, self
|
|
267
|
+
)
|
|
268
|
+
self._get_value_byte_size = MethodType(get_value_byte_size, self)
|
|
269
|
+
|
|
270
|
+
# Only assign validate_padding_bytes if not overridden in subclass
|
|
271
|
+
if type(self).validate_padding_bytes is SingleDecoder.validate_padding_bytes:
|
|
272
|
+
self.validate_padding_bytes = MethodType(
|
|
273
|
+
validate_padding_bytes_fixed_byte_size, self
|
|
274
|
+
)
|
|
275
|
+
|
|
284
276
|
def validate(self) -> None:
|
|
285
277
|
super().validate()
|
|
286
278
|
|
|
@@ -304,17 +296,14 @@ class FixedByteSizeDecoder(SingleDecoder):
|
|
|
304
296
|
raise ValueError("Value byte size exceeds data size")
|
|
305
297
|
|
|
306
298
|
def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
|
|
307
|
-
|
|
299
|
+
raise NotImplementedError("didnt call __init__")
|
|
308
300
|
|
|
309
301
|
def split_data_and_padding(self, raw_data: bytes) -> Tuple[bytes, bytes]:
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
|
|
313
|
-
validate_padding_bytes_fixed_byte_size(self, value, padding_bytes)
|
|
302
|
+
raise NotImplementedError("didnt call __init__")
|
|
314
303
|
|
|
304
|
+
# This is unused, but it is kept in to preserve the eth-abi api
|
|
315
305
|
def _get_value_byte_size(self) -> int:
|
|
316
|
-
|
|
317
|
-
return get_value_byte_size(self)
|
|
306
|
+
raise NotImplementedError("didnt call __init__")
|
|
318
307
|
|
|
319
308
|
|
|
320
309
|
class Fixed32ByteSizeDecoder(FixedByteSizeDecoder):
|
|
@@ -363,27 +352,29 @@ decode_uint_256 = UnsignedIntegerDecoder(value_bit_size=256)
|
|
|
363
352
|
class SignedIntegerDecoder(Fixed32ByteSizeDecoder):
|
|
364
353
|
is_big_endian = True
|
|
365
354
|
|
|
366
|
-
def
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return value
|
|
355
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
356
|
+
super().__init__(*args, **kwargs)
|
|
357
|
+
|
|
358
|
+
# Only assign validate_padding_bytes if not overridden in subclass
|
|
359
|
+
if type(self).validate_padding_bytes is SignedIntegerDecoder.validate_padding_bytes:
|
|
360
|
+
self.validate_padding_bytes = MethodType(validate_padding_bytes_signed_integer, self)
|
|
373
361
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
362
|
+
@cached_property
|
|
363
|
+
def neg_threshold(self) -> int:
|
|
364
|
+
return int(2 ** (self.value_bit_size - 1))
|
|
377
365
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
expected_padding_bytes = b"\xff" * padding_size
|
|
366
|
+
@cached_property
|
|
367
|
+
def neg_offset(self) -> int:
|
|
368
|
+
return int(2**self.value_bit_size)
|
|
382
369
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
370
|
+
def decoder_fn(self, data: bytes) -> int:
|
|
371
|
+
value = big_endian_to_int(data)
|
|
372
|
+
if value >= self.neg_threshold:
|
|
373
|
+
value -= self.neg_offset
|
|
374
|
+
return value
|
|
375
|
+
|
|
376
|
+
def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
|
|
377
|
+
return validate_padding_bytes_signed_integer(self, value, padding_bytes)
|
|
387
378
|
|
|
388
379
|
@parse_type_str("int")
|
|
389
380
|
def from_type_str(cls, abi_type, registry):
|
|
@@ -397,7 +388,7 @@ class BytesDecoder(Fixed32ByteSizeDecoder):
|
|
|
397
388
|
is_big_endian = False
|
|
398
389
|
|
|
399
390
|
@staticmethod
|
|
400
|
-
def decoder_fn(data):
|
|
391
|
+
def decoder_fn(data: bytes) -> bytes:
|
|
401
392
|
return data
|
|
402
393
|
|
|
403
394
|
@parse_type_str("bytes")
|
|
@@ -408,6 +399,10 @@ class BytesDecoder(Fixed32ByteSizeDecoder):
|
|
|
408
399
|
class BaseFixedDecoder(Fixed32ByteSizeDecoder):
|
|
409
400
|
frac_places: int = None
|
|
410
401
|
is_big_endian = True
|
|
402
|
+
|
|
403
|
+
@cached_property
|
|
404
|
+
def denominator(self) -> decimal.Decimal:
|
|
405
|
+
return TEN**self.frac_places
|
|
411
406
|
|
|
412
407
|
def validate(self) -> None:
|
|
413
408
|
super().validate()
|
|
@@ -417,15 +412,16 @@ class BaseFixedDecoder(Fixed32ByteSizeDecoder):
|
|
|
417
412
|
raise ValueError("must specify `frac_places`")
|
|
418
413
|
|
|
419
414
|
if frac_places <= 0 or frac_places > 80:
|
|
420
|
-
raise ValueError("`frac_places` must be in range (0, 80
|
|
415
|
+
raise ValueError("`frac_places` must be in range (0, 80)")
|
|
421
416
|
|
|
422
417
|
|
|
423
418
|
class UnsignedFixedDecoder(BaseFixedDecoder):
|
|
424
|
-
|
|
419
|
+
|
|
420
|
+
def decoder_fn(self, data: bytes) -> decimal.Decimal:
|
|
425
421
|
value = big_endian_to_int(data)
|
|
426
422
|
|
|
427
423
|
with decimal.localcontext(abi_decimal_context):
|
|
428
|
-
decimal_value = decimal.Decimal(value) /
|
|
424
|
+
decimal_value = decimal.Decimal(value) / self.denominator
|
|
429
425
|
|
|
430
426
|
return decimal_value
|
|
431
427
|
|
|
@@ -437,27 +433,42 @@ class UnsignedFixedDecoder(BaseFixedDecoder):
|
|
|
437
433
|
|
|
438
434
|
|
|
439
435
|
class SignedFixedDecoder(BaseFixedDecoder):
|
|
440
|
-
|
|
436
|
+
|
|
437
|
+
@cached_property
|
|
438
|
+
def neg_threshold(self) -> int:
|
|
439
|
+
return int(2 ** (self.value_bit_size - 1))
|
|
440
|
+
|
|
441
|
+
@cached_property
|
|
442
|
+
def neg_offset(self) -> int:
|
|
443
|
+
return int(2**self.value_bit_size)
|
|
444
|
+
|
|
445
|
+
@cached_property
|
|
446
|
+
def expected_padding_pos(self) -> bytes:
|
|
447
|
+
value_byte_size = get_value_byte_size(self)
|
|
448
|
+
padding_size = self.data_byte_size - value_byte_size
|
|
449
|
+
return b"\x00" * padding_size
|
|
450
|
+
|
|
451
|
+
@cached_property
|
|
452
|
+
def expected_padding_neg(self) -> bytes:
|
|
453
|
+
value_byte_size = get_value_byte_size(self)
|
|
454
|
+
padding_size = self.data_byte_size - value_byte_size
|
|
455
|
+
return b"\xff" * padding_size
|
|
456
|
+
|
|
457
|
+
def decoder_fn(self, data: bytes) -> decimal.Decimal:
|
|
441
458
|
value = big_endian_to_int(data)
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
signed_value = value - 2**value_bit_size
|
|
445
|
-
else:
|
|
446
|
-
signed_value = value
|
|
459
|
+
if value >= self.neg_threshold:
|
|
460
|
+
value -= self.neg_offset
|
|
447
461
|
|
|
448
462
|
with decimal.localcontext(abi_decimal_context):
|
|
449
|
-
decimal_value = decimal.Decimal(
|
|
463
|
+
decimal_value = decimal.Decimal(value) / self.denominator
|
|
450
464
|
|
|
451
465
|
return decimal_value
|
|
452
466
|
|
|
453
467
|
def validate_padding_bytes(self, value: Any, padding_bytes: bytes) -> None:
|
|
454
|
-
value_byte_size = get_value_byte_size(self)
|
|
455
|
-
padding_size = self.data_byte_size - value_byte_size
|
|
456
|
-
|
|
457
468
|
if value >= 0:
|
|
458
|
-
expected_padding_bytes =
|
|
469
|
+
expected_padding_bytes = self.expected_padding_pos
|
|
459
470
|
else:
|
|
460
|
-
expected_padding_bytes =
|
|
471
|
+
expected_padding_bytes = self.expected_padding_neg
|
|
461
472
|
|
|
462
473
|
if padding_bytes != expected_padding_bytes:
|
|
463
474
|
raise NonEmptyPaddingBytes(
|
|
@@ -478,7 +489,7 @@ class ByteStringDecoder(SingleDecoder):
|
|
|
478
489
|
is_dynamic = True
|
|
479
490
|
|
|
480
491
|
@staticmethod
|
|
481
|
-
def decoder_fn(data):
|
|
492
|
+
def decoder_fn(data: bytes) -> bytes:
|
|
482
493
|
return data
|
|
483
494
|
|
|
484
495
|
def read_data_from_stream(self, stream: ContextFramesBytesIO) -> bytes:
|
faster_eth_abi/encoding.py
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import codecs
|
|
3
3
|
import decimal
|
|
4
|
+
from functools import (
|
|
5
|
+
cached_property,
|
|
6
|
+
)
|
|
7
|
+
from types import (
|
|
8
|
+
MethodType,
|
|
9
|
+
)
|
|
4
10
|
from typing import (
|
|
5
11
|
Any,
|
|
6
12
|
Callable,
|
|
7
13
|
ClassVar,
|
|
14
|
+
Final,
|
|
8
15
|
NoReturn,
|
|
9
16
|
Optional,
|
|
10
17
|
Sequence,
|
|
11
18
|
Tuple,
|
|
12
19
|
Type,
|
|
20
|
+
final,
|
|
13
21
|
)
|
|
14
22
|
|
|
15
23
|
from faster_eth_utils import (
|
|
@@ -32,7 +40,11 @@ from faster_eth_abi._encoding import (
|
|
|
32
40
|
encode_fixed,
|
|
33
41
|
encode_signed,
|
|
34
42
|
encode_tuple,
|
|
43
|
+
encode_tuple_all_dynamic,
|
|
44
|
+
encode_tuple_no_dynamic,
|
|
45
|
+
encode_tuple_no_dynamic_funcs,
|
|
35
46
|
int_to_big_endian,
|
|
47
|
+
validate_tuple,
|
|
36
48
|
)
|
|
37
49
|
from faster_eth_abi.base import (
|
|
38
50
|
BaseCoder,
|
|
@@ -111,7 +123,33 @@ class TupleEncoder(BaseEncoder):
|
|
|
111
123
|
def __init__(self, encoders: Tuple[BaseEncoder, ...], **kwargs: Any) -> None:
|
|
112
124
|
super().__init__(encoders=encoders, **kwargs)
|
|
113
125
|
|
|
114
|
-
self.
|
|
126
|
+
self._is_dynamic: Final = tuple(getattr(e, "is_dynamic", False) for e in self.encoders)
|
|
127
|
+
self.is_dynamic = any(self._is_dynamic)
|
|
128
|
+
|
|
129
|
+
validators = []
|
|
130
|
+
for encoder in self.encoders:
|
|
131
|
+
try:
|
|
132
|
+
validator = encoder.validate_value
|
|
133
|
+
except AttributeError:
|
|
134
|
+
validators.append(encoder)
|
|
135
|
+
else:
|
|
136
|
+
validators.append(validator)
|
|
137
|
+
|
|
138
|
+
self.validators: Final[Callable[[Any], None]] = tuple(validators)
|
|
139
|
+
|
|
140
|
+
if type(self).encode is TupleEncoder.encode:
|
|
141
|
+
encode_func = (
|
|
142
|
+
encode_tuple_all_dynamic
|
|
143
|
+
if all(self._is_dynamic)
|
|
144
|
+
else encode_tuple_no_dynamic_funcs.get(
|
|
145
|
+
len(self.encoders),
|
|
146
|
+
encode_tuple_no_dynamic,
|
|
147
|
+
)
|
|
148
|
+
if not self.is_dynamic
|
|
149
|
+
else encode_tuple
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
self.encode = MethodType(encode_func, self)
|
|
115
153
|
|
|
116
154
|
def validate(self) -> None:
|
|
117
155
|
super().validate()
|
|
@@ -119,33 +157,15 @@ class TupleEncoder(BaseEncoder):
|
|
|
119
157
|
if self.encoders is None:
|
|
120
158
|
raise ValueError("`encoders` may not be none")
|
|
121
159
|
|
|
160
|
+
@final
|
|
122
161
|
def validate_value(self, value: Sequence[Any]) -> None:
|
|
123
|
-
|
|
124
|
-
self.invalidate_value(
|
|
125
|
-
value,
|
|
126
|
-
msg="must be list-like object such as array or tuple",
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
encoders = self.encoders
|
|
130
|
-
if len(value) != len(encoders):
|
|
131
|
-
self.invalidate_value(
|
|
132
|
-
value,
|
|
133
|
-
exc=ValueOutOfBounds,
|
|
134
|
-
msg=f"value has {len(value)} items when {len(encoders)} "
|
|
135
|
-
"were expected",
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
for item, encoder in zip(value, encoders):
|
|
139
|
-
try:
|
|
140
|
-
encoder.validate_value(item)
|
|
141
|
-
except AttributeError:
|
|
142
|
-
encoder(item)
|
|
162
|
+
validate_tuple(self, value)
|
|
143
163
|
|
|
144
164
|
def encode(self, values: Sequence[Any]) -> bytes:
|
|
145
|
-
self
|
|
146
|
-
return encode_tuple(values, self.encoders)
|
|
165
|
+
return encode_tuple(self, values)
|
|
147
166
|
|
|
148
|
-
__call__:
|
|
167
|
+
def __call__(self, values: Sequence[Any]) -> bytes:
|
|
168
|
+
return self.encode(values)
|
|
149
169
|
|
|
150
170
|
@parse_tuple_type_str
|
|
151
171
|
def from_type_str(cls, abi_type, registry):
|
|
@@ -328,11 +348,19 @@ class BaseFixedEncoder(NumberEncoder):
|
|
|
328
348
|
|
|
329
349
|
return False
|
|
330
350
|
|
|
351
|
+
@cached_property
|
|
352
|
+
def denominator(self) -> decimal.Decimal:
|
|
353
|
+
return TEN**self.frac_places
|
|
354
|
+
|
|
355
|
+
@cached_property
|
|
356
|
+
def precision(self) -> int:
|
|
357
|
+
return TEN**-self.frac_places
|
|
358
|
+
|
|
331
359
|
def validate_value(self, value):
|
|
332
360
|
super().validate_value(value)
|
|
333
361
|
|
|
334
362
|
with decimal.localcontext(abi_decimal_context):
|
|
335
|
-
residue = value %
|
|
363
|
+
residue = value % self.precision
|
|
336
364
|
|
|
337
365
|
if residue > 0:
|
|
338
366
|
self.invalidate_value(
|
|
@@ -359,7 +387,7 @@ class UnsignedFixedEncoder(BaseFixedEncoder):
|
|
|
359
387
|
|
|
360
388
|
def encode_fn(self, value):
|
|
361
389
|
with decimal.localcontext(abi_decimal_context):
|
|
362
|
-
scaled_value = value *
|
|
390
|
+
scaled_value = value * self.denominator
|
|
363
391
|
integer_value = int(scaled_value)
|
|
364
392
|
|
|
365
393
|
return int_to_big_endian(integer_value)
|
|
@@ -392,7 +420,7 @@ class SignedFixedEncoder(BaseFixedEncoder):
|
|
|
392
420
|
|
|
393
421
|
def encode_fn(self, value):
|
|
394
422
|
with decimal.localcontext(abi_decimal_context):
|
|
395
|
-
scaled_value = value *
|
|
423
|
+
scaled_value = value * self.denominator
|
|
396
424
|
integer_value = int(scaled_value)
|
|
397
425
|
|
|
398
426
|
unsigned_integer_value = integer_value % (2**self.value_bit_size)
|
|
Binary file
|
|
Binary file
|