Skip to content

feat: add AsyncEthereumClient mirroring EthereumClient#2523

Open
Uxio0 wants to merge 3 commits into
mainfrom
uxio/pla-1596-safe-eth-py-add-asyncethereumclient-mirroring-ethereumclient
Open

feat: add AsyncEthereumClient mirroring EthereumClient#2523
Uxio0 wants to merge 3 commits into
mainfrom
uxio/pla-1596-safe-eth-py-add-asyncethereumclient-mirroring-ethereumclient

Conversation

@Uxio0
Copy link
Copy Markdown
Member

@Uxio0 Uxio0 commented Jun 5, 2026

Summary

Adds an asyncio-based AsyncEthereumClient that mirrors EthereumClient's feature set using AsyncWeb3/AsyncHTTPProvider + aiohttp, while refactoring EthereumClient so the two share all I/O-free logic instead of duplicating it. Also fixes three bugs found during the refactor.

Closes PLA-1596.

Async client

  • AsyncEthereumClient(EthereumClient) and async managers subclass the sync ones, overriding only the I/O with async_-prefixed coroutines; every pure helper (payload building, response decoding, tx error mapping, trace selectors, manager builders/formatters) is reused, so no business logic is duplicated.
  • AsyncMulticall(Multicall) overrides only the on-chain eth_call, so async_batch_call / async_batch_call_same_function keep full parity with the sync client: Multicall when available, JSON-RPC batch fallback, force_batch_call honored, and identical results — including raw revert bytes for failed calls.
  • Per-event-loop aiohttp session with aclose() / async with lifecycle; POA middleware mirrored. Async tracing uses raw JSON-RPC (AsyncWeb3 has no .tracing).
  • Exported from safe_eth.eth alongside get_auto_async_ethereum_client.

Refactor (EthereumClient / Multicall)

Extracted shared, I/O-free helpers used by both sync and async clients: JSON-RPC payload builders, batch-response processing/validation, eth_call decoding, tx error mapping, trace selectors, ERC20/721 builders & formatters, and Multicall result decoding.

Bug fixes

  • get_total_transfer_history double-counted a transfer matching both the from and to filters — now deduplicated on (transactionHash, logIndex).
  • send_unsigned_transaction returned an empty HexBytes("") (looked like success) when retries were exhausted — now raises.
  • functools.cache on instance methods (get_chain_id, get_client_version, is_eip1559_supported, get_singleton_factory_address) pinned every client instance for the process lifetime — replaced with a per-instance cache + clear_cache().

Tests

  • The existing sync suites (TestERC20Module, TestTracingManager, TestEthereumClient) are reused against the async client via a small SyncCallProxy, so the async I/O paths are exercised by the same assertions; only the handful of tests that mock synchronous internals are overridden to target the async equivalent.
  • Added a sync-vs-async batch_call parity test (including the revert case).
  • Full suite green against ganache: ethereum_client (sync), async_ethereum_client, multicall, and the Safe test base setUp — 114 passed.

Notes

  • requests.Session thread-safety, get_balances native-balance N+1, and an async get_transfer_history are tracked as follow-ups (PLA-1598, PLA-1599, PLA-1600).

Add an asyncio-based AsyncEthereumClient that mirrors EthereumClient using
AsyncWeb3/AsyncHTTPProvider + aiohttp, subclassing the sync client and managers
and overriding only I/O so no business logic is duplicated. AsyncMulticall keeps
batch_call at full parity with the sync Multicall path (raw revert bytes included).

Refactor EthereumClient to extract I/O-free helpers shared by both clients
(payload builders, batch-response processing, eth_call decode, tx error mapping,
trace selectors, manager builders/formatters).

Reuse the sync test suites against the async client via a sync-facade proxy.

Fix bugs found during the refactor:
- get_total_transfer_history double-counted transfers matching both from/to filters
- send_unsigned_transaction returned an empty hash on exhausted retries (now raises)
- functools.cache on instance methods leaked instances (per-instance cache + clear_cache)
@Uxio0 Uxio0 requested a review from a team as a code owner June 5, 2026 13:16
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d0f7c2c57

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread safe_eth/eth/async_ethereum_client.py
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.

1 participant