Skip to content

Commit

Permalink
feat: find contract creation using debug namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
banteg committed Apr 18, 2024
1 parent 9c616f1 commit 9645c92
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions src/ape/managers/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,24 @@ def find_creation_block(lo, hi):
return None

block = find_creation_block(0, self.chain_manager.blocks.height)
traces = self.provider._make_request("trace_replayBlockTransactions", [block, ["trace"]])

# iterate over transaction traces in a block to find the deploy transaction
# this method also supports contract factories
if "geth" in self.provider.client_version.lower():
yield from self._find_creation_in_block_via_geth(block, query.contract)
else:
yield from self._find_creation_in_block_via_parity(block, query.contract)

def _find_creation_in_block_via_parity(self, block, contract_address):
# requires trace namespace
traces = self.provider._make_request("trace_replayBlockTransactions", [block, ["trace"]])

for tx in traces:
for trace in tx["trace"]:
if (
"error" not in trace
and trace["type"] == "create"
and trace["result"]["address"] == query.contract.lower()
and trace["result"]["address"] == contract_address.lower()
):
receipt = self.chain_manager.get_receipt(tx["transactionHash"])
creator = self.conversion_manager.convert(trace["action"]["from"], AddressType)
Expand All @@ -122,6 +130,41 @@ def find_creation_block(lo, hi):
factory=creator if creator != receipt.sender else None,
)

def _find_creation_in_block_via_geth(self, block, contract_address):
# requires debug namespace
traces = self.provider._make_request(
"debug_traceBlockByNumber", [hex(block), {"tracer": "callTracer"}]
)

def flatten(call):
if call["type"] in ["CREATE", "CREATE2"]:
yield call["from"], call["to"]

if "error" in call or "calls" not in call:
return

for sub in call["calls"]:
if sub["type"] in ["CREATE", "CREATE2"]:
yield sub["from"], sub["to"]
else:
yield from flatten(sub)

for tx in traces:
call = tx["result"]
sender = call["from"]
for factory, contract in flatten(call):
if contract == contract_address.lower():
yield ContractCreation(
txn_hash=tx["txHash"],
block=block,
deployer=self.conversion_manager.convert(sender, AddressType),
factory=(
self.conversion_manager.convert(factory, AddressType)
if factory != sender
else None
),
)

@perform_query.register
def perform_contract_events_query(self, query: ContractEventQuery) -> Iterator[ContractLog]:
addresses = query.contract
Expand Down

0 comments on commit 9645c92

Please sign in to comment.