Skip to content

Commit

Permalink
Reduce blocking (#9)
Browse files Browse the repository at this point in the history
* chore: bump eth_retry version

* feat: reduce blocking by calcs that can be delegated to subprocesses

* feat: remove _patch_tx

* chore: add light comments

* fix(test): fix test_brownie_patch
  • Loading branch information
BobTheBuidler authored May 27, 2022
1 parent 1616f30 commit 62b5595
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 316 deletions.
228 changes: 0 additions & 228 deletions dank_mids/brownie_patch/account.py

This file was deleted.

75 changes: 51 additions & 24 deletions dank_mids/brownie_patch/call.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,77 @@

import functools
from types import MethodType
from typing import Coroutine, Dict, Tuple, Union
from typing import Any, Coroutine, Dict, Tuple, Union

import eth_abi
from brownie.convert.normalize import format_input, format_output
from brownie.convert.utils import get_type_strings
from brownie.exceptions import VirtualMachineError
from brownie.network.contract import ContractCall, _get_tx
from brownie.network.contract import ContractCall
from brownie.project.compiler.solidity import SOLIDITY_ERROR_CODES
from hexbytes import HexBytes
from multicall.utils import run_in_subprocess
from web3 import Web3


def __encode_input(abi: Dict, signature: str, *args: Tuple) -> str:
data = format_input(abi, args)
types_list = get_type_strings(abi["inputs"])
return signature + eth_abi.encode_abi(types_list, data).hex()

def __decode_output(hexstr: str, abi: Dict) -> Any:
selector = HexBytes(hexstr)[:4].hex()
if selector == "0x08c379a0":
revert_str = eth_abi.decode_abi(["string"], HexBytes(hexstr)[4:])[0]
raise ValueError(f"Call reverted: {revert_str}")
elif selector == "0x4e487b71":
error_code = int(HexBytes(hexstr)[4:].hex(), 16)
if error_code in SOLIDITY_ERROR_CODES:
revert_str = SOLIDITY_ERROR_CODES[error_code]
else:
revert_str = f"Panic (error code: {error_code})"
raise ValueError(f"Call reverted: {revert_str}")
if abi["outputs"] and not hexstr:
raise ValueError("No data was returned - the call likely reverted")

types_list = get_type_strings(abi["outputs"])
result = eth_abi.decode_abi(types_list, HexBytes(hexstr))
result = format_output(abi, result)
if len(result) == 1:
result = result[0]
return result

def _patch_call(call: ContractCall, w3: Web3) -> None:
async def _encode_input(self, *args):
return await run_in_subprocess(
__encode_input,
self.abi,
self.signature,
*(arg if not hasattr(arg, 'address') else arg.address for arg in args)
)

async def _decode_output(self, data):
return await run_in_subprocess(__decode_output, data, self.abi)

@functools.wraps(call)
async def coroutine(
self,
*args: Tuple,
block_identifier: Union[int, str, bytes] = None,
override: Dict = None
) -> Coroutine:

args, tx = _get_tx(self._owner, args)
if tx["from"]:
tx["from"] = str(tx["from"])
del tx["required_confs"]
tx.update({"to": self._address, "data": self.encode_input(*args)})
if override:
raise ValueError("Cannot use state override with `coroutine`.")

calldata = await self._encode_input(*args)

try:
data = await w3.eth.call({k: v for k, v in tx.items() if v}, block_identifier, override)
data = await w3.eth.call({"to": self._address, "data": calldata}, block_identifier, override)
except ValueError as e:
raise VirtualMachineError(e) from None

selector = HexBytes(data)[:4].hex()

if selector == "0x08c379a0":
revert_str = eth_abi.decode_abi(["string"], HexBytes(data)[4:])[0]
raise ValueError(f"Call reverted: {revert_str}")
elif selector == "0x4e487b71":
error_code = int(HexBytes(data)[4:].hex(), 16)
if error_code in SOLIDITY_ERROR_CODES:
revert_str = SOLIDITY_ERROR_CODES[error_code]
else:
revert_str = f"Panic (error code: {error_code})"
raise ValueError(f"Call reverted: {revert_str}")
if self.abi["outputs"] and not data:
raise ValueError("No data was returned - the call likely reverted")
return self.decode_output(data)
return await self._decode_output(data)

call.coroutine = MethodType(coroutine, call)
call._encode_input = MethodType(_encode_input, call)
call._decode_output = MethodType(_decode_output, call)
4 changes: 0 additions & 4 deletions dank_mids/brownie_patch/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@
from brownie.network.contract import ContractCall, ContractTx, OverloadedMethod
from dank_mids.brownie_patch.call import _patch_call
from dank_mids.brownie_patch.overloaded import _patch_overloaded_method
from dank_mids.brownie_patch.tx import _patch_tx
from web3 import Web3

ContractMethod = Union[ContractCall, ContractTx, OverloadedMethod]

def _patch_if_method(method: ContractMethod, w3: Web3):
if isinstance(method, ContractCall) or isinstance(method, ContractTx):
_patch_call(method, w3)
# TODO implement this properly
#elif isinstance(method, ContractTx):
# _patch_tx(method, w3)
elif isinstance(method, OverloadedMethod):
_patch_overloaded_method(method, w3)

Expand Down
1 change: 0 additions & 1 deletion dank_mids/brownie_patch/overloaded.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from brownie.network.contract import ContractCall, ContractTx, OverloadedMethod
from dank_mids.brownie_patch.call import _patch_call
from dank_mids.brownie_patch.tx import _patch_tx
from web3 import Web3


Expand Down
Loading

0 comments on commit 62b5595

Please sign in to comment.