Skip to content

Commit

Permalink
fix: issues with call traces
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey committed Jan 16, 2024
1 parent 5732b31 commit 56d7428
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 16 deletions.
18 changes: 8 additions & 10 deletions src/ape_ethereum/ecosystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ def _enrich_calltree(self, call: Dict, **kwargs) -> Dict:
# Figure out the contract.
address = call.pop("address", "")
try:
call["contract_id"] = str(self.decode_address(address))
call["contract_id"] = address = str(self.decode_address(address))
except Exception:
# Tx was made with a weird address.
call["contract_id"] = address
Expand All @@ -833,7 +833,11 @@ def _enrich_calltree(self, call: Dict, **kwargs) -> Dict:

return {"contract_id": f"{address_int}", "calls": call["calls"]}

call["contract_id"] = self._enrich_contract_id(call["contract_id"], **kwargs)
depth = call.get("depth", 0)
if depth == 0 and address in self.account_manager:
call["contract_id"] = "Transferring ETH"
else:
call["contract_id"] = self._enrich_contract_id(call["contract_id"], **kwargs)

if not (contract_type := self.chain_manager.contracts.get(address)):
# Without a contract, we can enrich no further.
Expand Down Expand Up @@ -881,7 +885,7 @@ def _enrich_contract_id(self, address: AddressType, **kwargs) -> str:
return "ZERO_ADDRESS"

if not (contract_type := self.chain_manager.contracts.get(address)):
return self._contract_id_or_transfer(address)
return address

elif kwargs.get("use_symbol_for_tokens") and "symbol" in contract_type.view_methods:
# Use token symbol as name
Expand All @@ -905,13 +909,7 @@ def _enrich_contract_id(self, address: AddressType, **kwargs) -> str:
return str(symbol)

name = contract_type.name.strip() if contract_type.name else None
return name or self._contract_id_or_transfer(address)

def _contract_id_or_transfer(self, address: "AddressType") -> str:
if address in self.account_manager:
return f"Transferring {self.fee_token_symbol}"

return address
return name or address

def _enrich_calldata(
self,
Expand Down
7 changes: 6 additions & 1 deletion src/ape_ethereum/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class Web3Provider(ProviderAPI, ABC):
NOTE: This gets set in `ape_ethereum.trace.Trace`.
"""
_call_trace_approach: Optional[CallTraceApproach] = None
_supports_debug_trace_call: Optional[bool] = None

def __init__(self, *args, **kwargs):
logger.create_logger("web3.RequestManager", handlers=(_sanitize_web3_url,))
Expand Down Expand Up @@ -373,7 +374,11 @@ def send_call(
# Else, the table is difficult to understand.
use_symbol_for_tokens = track_gas or show_gas

trace = CallTrace(arguments=arguments, use_symbol_for_tokens=use_symbol_for_tokens)
trace = CallTrace(
arguments=arguments,
use_symbol_for_tokens=use_symbol_for_tokens,
supports_debug_trace_call=self._supports_debug_trace_call,
)

if track_gas and self._test_runner is not None and txn.receiver:
self._test_runner.gas_tracker.append_gas(trace, txn.receiver)
Expand Down
26 changes: 22 additions & 4 deletions src/ape_ethereum/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,37 +312,55 @@ def _trace_transaction(self) -> CallTreeNode:
def _set_approach(self, approach: CallTraceApproach):
self.call_trace_approach = approach
if hasattr(self.provider, "_call_trace_approach"):
self.provider._call_trace_approach = True
self.provider._call_trace_approach = approach


class CallTrace(Trace):
arguments: List[Any]

"""debug_traceCall must use the struct-log tracer."""
call_trace_approach: CallTraceApproach = CallTraceApproach.GETH_STRUCT_LOG_PARSE
supports_debug_trace_call: Optional[bool] = None

@property
def raw_trace_frames(self) -> List[Dict]:
return self._traced_call.get("structLogs", [])

@property
def return_value(self) -> Any:
return self._traced_call["returnValue"]
return self._traced_call.get("returnValue", "")

@cached_property
def _traced_call(self) -> Dict:
return self.provider.make_request("debug_traceCall", self.arguments)
if self.supports_debug_trace_call is True:
return self.provider.make_request("debug_traceCall", self.arguments)
elif self.supports_debug_trace_call is False:
return {}

try:
result = self.provider.make_request("debug_traceCall", self.arguments)
except Exception:
self._set_supports_trace_call(False)
return {}

self._set_supports_trace_call(True)
return result

@property
def transaction(self) -> Dict:
return self.arguments[0]
return self.arguments[0] if self.arguments else {}

def get_calltree(self) -> CallTreeNode:
calltree = self._debug_trace_transaction_struct_logs_to_call()
calltree.gas_cost = self._traced_call.get("gas", calltree.gas_cost)
calltree.failed = self._traced_call.get("failed", calltree.failed)
return calltree

def _set_supports_trace_call(self, value: bool):
self.supports_debug_trace_call = value
if hasattr(self.provider, "_supports_debug_trace_call"):
self.provider._supports_debug_trace_call = True


def parse_rich_tree(call: Dict, verbose: bool = False) -> Tree:
tree = _create_tree(call, verbose=verbose)
Expand Down
14 changes: 13 additions & 1 deletion tests/functional/test_trace.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from evm_trace import CallTreeNode, CallType

from ape_ethereum.trace import parse_rich_tree
from ape_ethereum.trace import CallTrace, parse_rich_tree


def test_parse_rich_tree(vyper_contract_instance):
Expand Down Expand Up @@ -45,3 +45,15 @@ def test_get_gas_report_transfer(gas_tracker, sender, receiver):
actual = trace.get_gas_report()
expected = {"Transferring ETH": {"to:TEST::2": [tx.gas_used]}}
assert actual == expected


def test_call_trace_debug_trace_call_not_supported(
eth_tester_provider, owner, vyper_contract_instance
):
"""
When using EthTester, we can still see the top-level trace of a call.
"""
tx = {"to": vyper_contract_instance.address, "from": owner.address}
trace = CallTrace(arguments=[tx])
actual = f"{trace}"
assert actual == "VyperContract.0x()"

0 comments on commit 56d7428

Please sign in to comment.