Skip to content

Add a way to fetch all contract events#222

Merged
alisinabh merged 8 commits into
ExWeb3:mainfrom
ddallaire:feat/get-contract-events
Jul 19, 2026
Merged

Add a way to fetch all contract events#222
alisinabh merged 8 commits into
ExWeb3:mainfrom
ddallaire:feat/get-contract-events

Conversation

@ddallaire

@ddallaire ddallaire commented Aug 24, 2025

Copy link
Copy Markdown
Contributor

Description

Using get_logs becomes tedious when ingesting all (or several) of a contract's events, since each event requires its own filter and request. This PR makes it possible to fetch and decode multiple events — or all events of a contract — in a single eth_getLogs request.

Following the review discussion, the original get_logs_for_contract function was reverted in favor of combined event filters: the combination is a filter value rather than a new function, so it works everywhere a regular event filter is accepted with no API surface changes to get_logs.

Details

Combining specific events

filter =
  Ethers.EventFilter.combine([
    MyToken.EventFilters.transfer(nil, nil),
    MyToken.EventFilters.approval(nil, nil)
  ])

{:ok, events} = Ethers.get_logs(filter, address: "0x...")

combine/1 returns an Ethers.CombinedEventFilter which sends the topic_0 values as an OR-ed list, so filtering happens server side (no client-side dropping of logs). Each fetched log is decoded with its matching event selector.

Fetching all events of a contract

Passing an EventFilters module is translated internally to a combined filter of all the contract's events (via a hidden generated __all__/0, following the __events__/__default_address__ convention to avoid colliding with contracts that define an All event):

{:ok, events} = Ethers.get_logs(MyToken.EventFilters, address: "0x...")

Module names are accepted by Ethers.get_logs/2, Ethers.batch/2 ({:get_logs, MyToken.EventFilters, ...}) and inside Ethers.EventFilter.combine/1 (mixed with regular filters).

Validation (at combine time)

combine/1 raises ArgumentError when:

  • the list of filters is empty (or the module has no events)
  • a filter has indexed-argument values — only wildcard filters can be combined, since OR-ing positional topics across events would match unintended logs
  • filters have conflicting default addresses — eth_getLogs accepts a single address

Bug fix

Batched {:get_logs, filter, from_block: ...} requests were broken since batching bypasses Ethereumex's from_blockfromBlock key normalization. pre_process now normalizes the keys itself.

Checklist

  • Add Ethers.CombinedEventFilter and Ethers.EventFilter.combine/1
  • Accept EventFilters modules in get_logs, batch and combine (translated to the generated __all__/0)
  • Add Ethers.Types.is_module/1 guard
  • Update technical documentation, README and CHANGELOG
  • Unit tests covering combine, module translation, batch, default addresses and all error paths

@ddallaire

Copy link
Copy Markdown
Contributor Author

@alisinabh Hey, love the library, I keep running into having to create a bunch of filters to ingest manually multiple events of the same contract, this aims to fix this, feel free to close it if you don't think it belongs here.

Add a way to fetch all contract events
@ddallaire
ddallaire force-pushed the feat/get-contract-events branch from 5cbd5d0 to b5ee972 Compare August 24, 2025 17:59
@ddallaire
ddallaire force-pushed the feat/get-contract-events branch from b5ee972 to efecb06 Compare August 24, 2025 18:02

@alisinabh alisinabh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the addition.

Upon first look I added one comment.

Also Im not sure a function in Ethers module is the best answer here. My initial thought is to have a function (maybe in Utils) that returns all event filters of a contract then the result of that can be used with the same get_logs function.

@ddallaire

ddallaire commented Aug 25, 2025

Copy link
Copy Markdown
Contributor Author

Thanks for the addition.

Upon first look I added one comment.

Also Im not sure a function in Ethers module is the best answer here. My initial thought is to have a function (maybe in Utils) that returns all event filters of a contract then the result of that can be used with the same get_logs function.

That sounds reasonable, could adapt the get_logs function to allow for multiple event filters, something like:

def get_logs(event_filters, overrides \\ []) when is_list(event_filters)

Or allow a list in the same function and have pre_process handle lists to split by topics.

Let me know what you think.

@alisinabh

Copy link
Copy Markdown
Member

Yeah I think pre_process/4 should handle that, the get_logs function just needs its documentation and spec and variable naming changes.

One caveat is that we should ensure that if there are more than one event filter with a default address they must be the same. Otherwise we should require an explicit address field as eth_getLogs does not support multiple addresses AFAIK.

alisinabh and others added 4 commits July 19, 2026 02:12
Keeps the Counter contract reset() event which the new tests reuse.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Introduce Ethers.CombinedEventFilter, created via Ethers.EventFilter.combine/1
or the generated EventFilters.all/0 function of contract modules. The combined
topic_0 values are sent as an OR-ed list so filtering happens server side, and
fetched logs are decoded with their matching event selector. Works with both
Ethers.get_logs/2 and Ethers.batch/2.

Combining validates that all filters use wildcard indexed arguments and have
no conflicting default addresses (eth_getLogs accepts a single address).

Also normalize from_block/to_block keys to fromBlock/toBlock in get_logs
pre-processing, fixing batched get_logs requests with block ranges which
bypassed Ethereumex's key normalization.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the generated EventFilters.all/0 function with a hidden __all__/0
(following the __events__/__default_address__ convention) to avoid colliding
with contracts that define an All event. Instead, Ethers.get_logs/2,
Ethers.batch/2 and Ethers.EventFilter.combine/1 now accept an EventFilters
module name directly and translate it to __all__/0 internally.

Also add the Ethers.Types.is_module/1 guard for module name detection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces combined event filters so consumers can fetch and decode logs for multiple events (OR semantics) — or all events of a contract — in a single eth_getLogs call, reducing the need for multiple event-specific requests.

Changes:

  • Add Ethers.CombinedEventFilter plus Ethers.EventFilter.combine/1 to OR multiple event selectors into a single filter and decode results per-event.
  • Extend Ethers.get_logs/2 and Ethers.batch/2 to accept an EventFilters module (fetch all events of a contract) and handle decoding for combined filters.
  • Update tests and docs (README/CHANGELOG) and add a new ResetCalled event to the test contract to validate multi-event behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/support/contracts/counter.sol Adds a reset function and ResetCalled() event to support multi-event log tests.
test/ethers/counter_contract_test.exs Adds coverage for combining filters, module-based “all events” fetching, batching, default address behavior, and failure cases.
test/ethers_test.exs Adds regression coverage for request failure handling with combined filters and default address mapping.
README.md Documents combining event filters and fetching all contract events via an EventFilters module.
lib/ethers/types.ex Adds an is_module/1 guard used to detect module arguments.
lib/ethers/event_filter.ex Adds combine/1 delegation and supports CombinedEventFilter in to_map/2.
lib/ethers/contract.ex Generates EventFilters.__all__/0 for contract modules to expose “all events” combined filter.
lib/ethers/combined_event_filter.ex Implements combined filter construction, validation, mapping to RPC params, decoding, and inspect formatting.
lib/ethers.ex Extends get_logs/2 + batch request normalization to accept EventFilters modules and decode combined-filter results.
CHANGELOG.md Announces the combined event filter feature and module-based “all events” usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/ethers.ex Outdated
Comment thread lib/ethers.ex
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@alisinabh

Copy link
Copy Markdown
Member

@ddallaire I have deviated a bit from the original approach and I believe this should be better. Thank you for the initial work on this.

@alisinabh
alisinabh merged commit ef30fc6 into ExWeb3:main Jul 19, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants