Skip to content

Commit 18f7660

Browse files
committed
feat: better multicall encoding/decoding
1 parent 62ee9c6 commit 18f7660

File tree

1 file changed

+29
-7
lines changed

1 file changed

+29
-7
lines changed

dank_mids/_requests.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from concurrent.futures.process import BrokenProcessPool
88
from contextlib import suppress
99
from functools import cached_property, lru_cache
10-
from itertools import chain
10+
from itertools import accumulate, chain
1111
from typing import (
1212
TYPE_CHECKING,
1313
Any,
@@ -27,10 +27,12 @@
2727
)
2828

2929
import a_sync
30+
import eth_abi.registry
3031
import eth_retry
3132
from a_sync import AsyncProcessPoolExecutor, PruningThreadPoolExecutor
3233
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
3436
from eth_typing import ChecksumAddress
3537
from eth_utils import function_signature_to_4byte_selector
3638
from hexbytes import HexBytes
@@ -600,20 +602,40 @@ def _record_failure(self, e: Exception, data: str) -> None:
600602
)
601603

602604

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
605609

606610

607611
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))
609622

610623

611624
def mcall_decode(data: PartialResponse) -> Union[List[Tuple[bool, bytes]], Exception]:
612625
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)
614636
except Exception as e:
615637
# 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
617639
return e
618640

619641

0 commit comments

Comments
 (0)