Skip to content

Commit

Permalink
fix: handle when log address is not checksummed (#2429)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Dec 20, 2024
1 parent 835e1a0 commit 2f9d83f
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 47 deletions.
11 changes: 7 additions & 4 deletions src/ape_ethereum/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,9 @@ def decode_logs(
contract_types = self.chain_manager.contracts.get_multiple(addresses)
# address → selector → abi
selectors = {
address: {encode_hex(keccak(text=abi.selector)): abi for abi in contract.events}
address.lower(): {
encode_hex(keccak(text=abi.selector)): abi for abi in contract.events
}
for address, contract in contract_types.items()
}

Expand All @@ -339,10 +341,11 @@ def get_default_log(
decoded_logs: ContractLogContainer = ContractLogContainer()
for log in self.logs:
if contract_address := log.get("address"):
if contract_address in selectors and (topics := log.get("topics")):
lower_address = contract_address.lower()
if lower_address in selectors and (topics := log.get("topics")):
selector = encode_hex(topics[0])
if selector in selectors[contract_address]:
event_abi = selectors[contract_address][selector]
if selector in selectors[lower_address]:
event_abi = selectors[lower_address][selector]
decoded_logs.extend(
self.provider.network.ecosystem.decode_logs([log], event_abi)
)
Expand Down
103 changes: 60 additions & 43 deletions tests/functional/test_receipt.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ def test_show_events(trace_print_capture, invoke_receipt):
assert "newNum=[bright_magenta]1" in label


def test_decode_logs(owner, contract_instance, assert_log_values):
event_type = contract_instance.NumberChange

# Invoke a transaction 3 times that generates 3 logs.
receipt_0 = contract_instance.setNumber(1, sender=owner)
receipt_1 = contract_instance.setNumber(2, sender=owner)
receipt_2 = contract_instance.setNumber(3, sender=owner)

def assert_receipt_logs(receipt: "ReceiptAPI", num: int):
logs = receipt.decode_logs(event_type)
assert len(logs) == 1
assert_log_values(logs[0], num)
assert receipt.timestamp == logs[0].timestamp

assert_receipt_logs(receipt_0, 1)
assert_receipt_logs(receipt_1, 2)
assert_receipt_logs(receipt_2, 3)


def test_decode_logs_specify_abi(invoke_receipt, vyper_contract_instance):
abi = vyper_contract_instance.NumberChange.abi
logs = invoke_receipt.decode_logs(abi=abi)
Expand All @@ -113,6 +132,47 @@ def test_decode_logs_specify_abi_as_event(
assert spy.call_count == 0


def test_decode_logs_multiple_event_types(owner, contract_instance, assert_log_values):
foo_happened = contract_instance.FooHappened
bar_happened = contract_instance.BarHappened
receipt = contract_instance.fooAndBar(sender=owner)
logs = receipt.decode_logs([foo_happened, bar_happened])
assert len(logs) == 2
assert logs[0].foo == 0
assert logs[1].bar == 1


def test_decode_logs_not_checksummed_addresses(owner, contract_instance, assert_log_values):
"""
If an RPC returns non-checksummed logs with non-checksummed addresses,
show we can still decode them.
"""
start_number = contract_instance.myNumber()
tx = contract_instance.setNumber(1, sender=owner)
assert len(tx.logs) == 1

# Hack to make the address lower-case.
tx.logs[0]["address"] = tx.logs[0]["address"].lower()

events = tx.decode_logs()
assert len(events) == 1
assert_log_values(events[0], 1, start_number)


def test_decode_logs_unspecified_abi_gets_all_logs(owner, contract_instance):
receipt = contract_instance.fooAndBar(sender=owner)
logs = receipt.decode_logs() # Same as doing `receipt.events`
assert len(logs) == 2
assert logs[0].foo == 0
assert logs[1].bar == 1


def test_events(owner, contract_instance, assert_log_values):
receipt = contract_instance.setNumber(1, sender=owner)
assert len(receipt.events) == 1
assert_log_values(receipt.events[0], 1)


def test_events_with_ds_notes(ds_note_test_contract, owner):
contract = ds_note_test_contract
receipt = contract.test_0(sender=owner)
Expand Down Expand Up @@ -143,49 +203,6 @@ def test_events_with_ds_notes(ds_note_test_contract, owner):
assert receipt.events[0].transaction_index == 0


def test_decode_logs(owner, contract_instance, assert_log_values):
event_type = contract_instance.NumberChange

# Invoke a transaction 3 times that generates 3 logs.
receipt_0 = contract_instance.setNumber(1, sender=owner)
receipt_1 = contract_instance.setNumber(2, sender=owner)
receipt_2 = contract_instance.setNumber(3, sender=owner)

def assert_receipt_logs(receipt: "ReceiptAPI", num: int):
logs = receipt.decode_logs(event_type)
assert len(logs) == 1
assert_log_values(logs[0], num)
assert receipt.timestamp == logs[0].timestamp

assert_receipt_logs(receipt_0, 1)
assert_receipt_logs(receipt_1, 2)
assert_receipt_logs(receipt_2, 3)


def test_events(owner, contract_instance, assert_log_values):
receipt = contract_instance.setNumber(1, sender=owner)
assert len(receipt.events) == 1
assert_log_values(receipt.events[0], 1)


def test_decode_logs_multiple_event_types(owner, contract_instance, assert_log_values):
foo_happened = contract_instance.FooHappened
bar_happened = contract_instance.BarHappened
receipt = contract_instance.fooAndBar(sender=owner)
logs = receipt.decode_logs([foo_happened, bar_happened])
assert len(logs) == 2
assert logs[0].foo == 0
assert logs[1].bar == 1


def test_decode_logs_unspecified_abi_gets_all_logs(owner, contract_instance):
receipt = contract_instance.fooAndBar(sender=owner)
logs = receipt.decode_logs() # Same as doing `receipt.events`
assert len(logs) == 2
assert logs[0].foo == 0
assert logs[1].bar == 1


def test_get_failed_receipt(owner, vyper_contract_instance, eth_tester_provider):
# Setting to '5' always fails.

Expand Down

0 comments on commit 2f9d83f

Please sign in to comment.