|
7 | 7 | from concurrent.futures.process import BrokenProcessPool |
8 | 8 | from contextlib import suppress |
9 | 9 | from functools import cached_property, lru_cache |
10 | | -from itertools import chain |
| 10 | +from itertools import accumulate, chain |
11 | 11 | from typing import ( |
12 | 12 | TYPE_CHECKING, |
13 | 13 | Any, |
|
27 | 27 | ) |
28 | 28 |
|
29 | 29 | import a_sync |
| 30 | +import eth_abi.registry |
30 | 31 | import eth_retry |
31 | 32 | from a_sync import AsyncProcessPoolExecutor, PruningThreadPoolExecutor |
32 | 33 | from aiohttp.client_exceptions import ClientResponseError |
33 | | -from eth_abi import abi, decoding |
| 34 | +from eth_abi import decoding |
| 35 | +from eth_abi.encoding import encode_uint_256 |
34 | 36 | from eth_typing import ChecksumAddress |
35 | 37 | from eth_utils import function_signature_to_4byte_selector |
36 | 38 | from hexbytes import HexBytes |
@@ -600,20 +602,40 @@ def _record_failure(self, e: Exception, data: str) -> None: |
600 | 602 | ) |
601 | 603 |
|
602 | 604 |
|
603 | | -mcall_encoder = abi.default_codec._registry.get_encoder("(bool,(address,bytes)[])") |
604 | | -mcall_decoder = abi.default_codec._registry.get_decoder("(uint256,uint256,(bool,bytes)[])") |
| 605 | +__mcall_item_encoder = eth_abi.registry.registry.get_encoder("(bool,(address,bytes)[])").encoders[1].item_encoder |
| 606 | +__decoder0, __decoder1, __decoder2 = eth_abi.registry.registry.get_decoder("(uint256,uint256,(bool,bytes)[])").decoders |
| 607 | + |
| 608 | +__32BYTE_TAIL_CHUNK = b"\00" * 32 |
605 | 609 |
|
606 | 610 |
|
607 | 611 | def mcall_encode(data: List[Tuple[bool, bytes]]) -> bytes: |
608 | | - return mcall_encoder([False, data]) |
| 612 | + assert data |
| 613 | + # encode packed array |
| 614 | + head_length = 32 * len(data) |
| 615 | + tail_chunks = tuple(__mcall_item_encoder(i) for i in data) |
| 616 | + tail_offsets = (0,) + tuple(accumulate(map(len, tail_chunks[:-1]))) |
| 617 | + head_chunks = tuple(encode_uint_256(head_length + offset) for offset in tail_offsets) |
| 618 | + encoded_array = b"".join(head_chunks + tail_chunks) |
| 619 | + # encode tuple |
| 620 | + head_length = 32 + len(encoded_array) |
| 621 | + return b"".join((encode_uint_256(head_length), encoded_array, __32BYTE_TAIL_CHUNK)) |
609 | 622 |
|
610 | 623 |
|
611 | 624 | def mcall_decode(data: PartialResponse) -> Union[List[Tuple[bool, bytes]], Exception]: |
612 | 625 | try: |
613 | | - return mcall_decoder(decoding.ContextFramesBytesIO(data.decode_result("eth_call")))[2] |
| 626 | + stream = decoding.ContextFramesBytesIO(data.decode_result("eth_call")) |
| 627 | + # NOTE eth_abi does this check that we intentionally skip because the data should just be right. Fingers crossed. |
| 628 | + # mcall_decoder.validate_pointers(stream) |
| 629 | + |
| 630 | + # discard the first field |
| 631 | + __decoder0(stream) |
| 632 | + # discard the second field |
| 633 | + __decoder1(stream) |
| 634 | + # return the third field |
| 635 | + return __decoder2(stream) |
614 | 636 | except Exception as e: |
615 | 637 | # NOTE: We need to safely bring any Exceptions back out of the ProcessPool |
616 | | - e.args = (*e.args, data.decode_result() if isinstance(data, PartialResponse) else data) |
| 638 | + e.args = *e.args, data.decode_result() if isinstance(data, PartialResponse) else data |
617 | 639 | return e |
618 | 640 |
|
619 | 641 |
|
|
0 commit comments