Skip to content

Commit

Permalink
perf: optimize trace return and revert values (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Aug 26, 2024
1 parent b58fcb3 commit d700b97
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 5 deletions.
7 changes: 2 additions & 5 deletions ape_alchemy/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from ape.logging import logger
from ape.types import BlockID
from ape_ethereum.provider import Web3Provider
from ape_ethereum.trace import TransactionTrace
from ape_ethereum.transactions import AccessList
from eth_pydantic_types import HexBytes
from eth_typing import HexStr
Expand All @@ -24,6 +23,7 @@
from web3.types import RPCEndpoint

from .exceptions import AlchemyFeatureNotAvailable, AlchemyProviderError, MissingProjectKeyError
from .trace import AlchemyTransactionTrace

# The user must either set one of these or an ENV VAR of the pattern:
# WEB3_<ECOSYSTEM>_<NETWORK>_PROJECT_ID or WEB3_<ECOSYSTEM>_<NETWORK>_API_KEY
Expand Down Expand Up @@ -134,10 +134,7 @@ def _get_prestate_trace(self, transaction_hash: str) -> dict:
)

def get_transaction_trace(self, transaction_hash: str, **kwargs) -> TraceAPI:
if "debug_trace_transaction_parameters" not in kwargs:
kwargs["debug_trace_transaction_parameters"] = {}

return TransactionTrace(transaction_hash=transaction_hash, **kwargs)
return AlchemyTransactionTrace(transaction_hash=transaction_hash, **kwargs)

def get_virtual_machine_error(self, exception: Exception, **kwargs) -> VirtualMachineError:
txn = kwargs.get("txn")
Expand Down
37 changes: 37 additions & 0 deletions ape_alchemy/trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from functools import cached_property
from typing import Any, Optional

from ape_ethereum.trace import TraceApproach, TransactionTrace
from hexbytes import HexBytes


class AlchemyTransactionTrace(TransactionTrace):
call_trace_approach: TraceApproach = TraceApproach.PARITY

@cached_property
def return_value(self) -> Any:
node = self._top_level_call
if output := node.get("output"):
output_bytes = HexBytes(output)
if abi := self.root_method_abi:
return self._ecosystem.decode_returndata(abi, output_bytes)

# ABI is not known.
return output_bytes

return None

@cached_property
def revert_message(self) -> Optional[str]:
node = self._top_level_call
return node.get("revertReason")

@cached_property
def _top_level_call(self) -> dict:
return self.provider.make_request(
"debug_traceTransaction",
[
self.transaction_hash,
{"tracer": "callTracer", "tracerConfig": {"onlyTopLevelCall": True}},
],
)
38 changes: 38 additions & 0 deletions tests/test_trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest
from ape import chain, networks
from ethpm_types import ContractType


@pytest.fixture(autouse=True)
def ethereum_mainnet_alchemy():
with networks.ethereum.mainnet.use_provider("alchemy"):
yield


def test_revert_message():
txn_hash = "0x36144f609e0fc7afd3cc570d6a54582091642a44c5223a5ad59aa20008dd9577"
receipt = chain.history[txn_hash]
actual = receipt.trace.revert_message
expected = "UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT"
assert actual == expected


def test_return_value():
txn_hash = "0xe0897d735b67893648b20085ecef16232733425329df844292d5b2774cca436b"
receipt = chain.history[txn_hash]

# Ensure the ABI is cached so we can decode the return value.
abi = [
{
"type": "function",
"name": "submit",
"stateMutability": "payable",
"inputs": [{"name": "_referral", "type": "address"}],
"outputs": [{"name": "", "type": "uint256"}],
}
]
chain.contracts[receipt.receiver] = ContractType(abi=abi)

actual = receipt.return_value
expected = 1244617160572980465
assert actual == expected

0 comments on commit d700b97

Please sign in to comment.