diff --git a/src/ape_ethereum/transactions.py b/src/ape_ethereum/transactions.py index 83ebbd57a3..b8e9828237 100644 --- a/src/ape_ethereum/transactions.py +++ b/src/ape_ethereum/transactions.py @@ -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() } @@ -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) ) diff --git a/tests/functional/test_receipt.py b/tests/functional/test_receipt.py index 1d99da80ec..0f66fecbf7 100644 --- a/tests/functional/test_receipt.py +++ b/tests/functional/test_receipt.py @@ -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) @@ -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) @@ -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.