Skip to content

Commit

Permalink
Parse debug_traceTransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielSchiavini committed May 8, 2024
1 parent 26e635f commit 09f0d4a
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 11 deletions.
5 changes: 3 additions & 2 deletions boa_zksync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ def set_zksync_test_env():


def set_zksync_fork(url):
boa.set_env(ZksyncEnv.from_url(url))
env = ZksyncEnv.from_url(url)
env.fork()
boa.set_env(env)
boa.set_deployer_class(ZksyncDeployer)
boa.env.fork()


def set_zksync_browser_env(address=None):
Expand Down
18 changes: 11 additions & 7 deletions boa_zksync/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from boa.util.abi import Address
from eth.exceptions import VMError
from eth_account import Account
from requests import HTTPError

from boa_zksync.node import EraTestNode
from boa_zksync.types import DeployTransaction, ZksyncComputation, ZksyncMessage
Expand Down Expand Up @@ -115,18 +116,21 @@ def execute_code(
args = ZksyncMessage(sender, to_address, gas or 0, value, data)

try:
trace_call = self._rpc.fetch("debug_traceCall", [args.as_json_dict(), "latest"])
traced_computation = ZksyncComputation.from_trace(trace_call)
except RPCError:
trace_call = self._rpc.fetch("debug_traceCall", [args.as_json_dict(), "latest", {"tracer": "callTracer"}])
traced_computation = ZksyncComputation.from_call_trace(trace_call)
except (RPCError, HTTPError):
output = self._rpc.fetch("eth_call", [args.as_json_dict(), "latest"])
traced_computation = ZksyncComputation(args, output)
traced_computation = ZksyncComputation(args, bytes.fromhex(output.removeprefix("0x")))

if is_modifying:
try:
receipt, trace = self._send_txn(**args.as_tx_params())
assert (
traced_computation.is_error == trace.is_error
), f"VMError mismatch: {traced_computation.error} != {trace.error}"
if trace:
assert (
traced_computation.is_error == trace.is_error
), f"VMError mismatch: {traced_computation.error} != {trace.error}"
return ZksyncComputation.from_debug_trace(trace.raw_trace)

except _EstimateGasFailed:
if not traced_computation.is_error: # trace gives more information
return ZksyncComputation(args, error=VMError("Estimate gas failed"))
Expand Down
21 changes: 19 additions & 2 deletions boa_zksync/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class ZksyncComputation:
children: list["ZksyncComputation"] = field(default_factory=list)

@classmethod
def from_trace(cls, output: dict) -> "ZksyncComputation":
def from_call_trace(cls, output: dict) -> "ZksyncComputation":
"""Recursively constructs a ZksyncComputation from a debug_traceCall output."""
error = None
if output.get("error") is not None:
Expand All @@ -235,9 +235,26 @@ def from_trace(cls, output: dict) -> "ZksyncComputation":
),
output=to_bytes(output["output"]),
error=error,
children=[cls.from_trace(call) for call in output.get("calls", [])],
children=[cls.from_call_trace(call) for call in output.get("calls", [])],
)

@classmethod
def from_debug_trace(cls, output: dict):
"""
Finds the actual transaction computation, since zksync has system contract calls in the trace.
"""
to, sender = output["to"], output["from"]

def _find(calls: list[dict]):
for trace in calls:
if found := _find(trace["calls"]):
return found
if trace["to"] == to and trace["from"] == sender:
return cls.from_call_trace(trace)

return _find(output["calls"])


@property
def is_success(self) -> bool:
"""
Expand Down

0 comments on commit 09f0d4a

Please sign in to comment.