diff --git a/brownie/network/state.py b/brownie/network/state.py index f617f70c0..3e3182dd3 100644 --- a/brownie/network/state.py +++ b/brownie/network/state.py @@ -14,7 +14,7 @@ from brownie._config import CONFIG, _get_data_folder from brownie._singleton import _Singleton from brownie.convert import Wei -from brownie.exceptions import BrownieEnvironmentError +from brownie.exceptions import BrownieEnvironmentError, CompilerError from brownie.network import rpc from brownie.project.build import DEPLOYMENT_KEYS from brownie.utils.sql import Cursor @@ -555,7 +555,7 @@ def _find_contract(address: Any) -> Any: from brownie.network.contract import Contract return Contract(address) - except ValueError: + except (ValueError, CompilerError): pass diff --git a/brownie/network/transaction.py b/brownie/network/transaction.py index 1f144ffe1..c72144753 100644 --- a/brownie/network/transaction.py +++ b/brownie/network/transaction.py @@ -10,6 +10,7 @@ from hashlib import sha1 from pathlib import Path from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union +from warnings import warn import black import requests @@ -657,12 +658,32 @@ def _get_trace(self) -> None: self._modified_state = False return - if isinstance(trace[0]["gas"], str): - # handle traces where numeric values are returned as hex (Nethermind) + # different nodes return slightly different formats. its really fun to handle + # geth/nethermind returns unprefixed and with 0-padding for stack and memory + # erigon returns 0x-prefixed and without padding (but their memory values are like geth) + fix_stack = False + for step in trace: + if not step["stack"]: + continue + check = step["stack"][0] + if not isinstance(check, str): + break + if check.startswith("0x"): + fix_stack = True + break + + fix_gas = isinstance(trace[0]["gas"], str) + + if fix_stack or fix_gas: for step in trace: - step["gas"] = int(step["gas"], 16) - step["gasCost"] = int.from_bytes(HexBytes(step["gasCost"]), "big", signed=True) - step["pc"] = int(step["pc"], 16) + if fix_stack: + # for stack values, we need 32 bytes (64 chars) without the 0x prefix + step["stack"] = [HexBytes(s).hex()[2:].zfill(64) for s in step["stack"]] + if fix_gas: + # handle traces where numeric values are returned as hex (Nethermind) + step["gas"] = int(step["gas"], 16) + step["gasCost"] = int.from_bytes(HexBytes(step["gasCost"]), "big", signed=True) + step["pc"] = int(step["pc"], 16) if self.status: self._confirmed_trace(trace) @@ -678,6 +699,9 @@ def _confirmed_trace(self, trace: Sequence) -> None: if contract: data = _get_memory(trace[-1], -1) fn = contract.get_method_object(self.input) + if not fn: + warn(f"Unable to find function on {contract} for input {self.input}") + return self._return_value = fn.decode_output(data) def _reverted_trace(self, trace: Sequence) -> None: @@ -978,8 +1002,11 @@ def _expand_trace(self) -> None: ) def _add_internal_xfer(self, from_: str, to: str, value: str) -> None: + if not value.startswith("0x"): + value = f"0x{value}" + self._internal_transfers.append( # type: ignore - {"from": EthAddress(from_), "to": EthAddress(to), "value": Wei(f"0x{value}")} + {"from": EthAddress(from_), "to": EthAddress(to), "value": Wei(value)} ) def _full_name(self) -> str: