EtherscanSource
Both source seams over one Etherscan V2 client.
balances(chain_id, address) -> list[BalanceRecord] and
fetch_txlist(chain_id, address, *, start_block, end_block, page, offset, sort) -> list[dict] of RAW rows for the decoder seam.
The client is injected: this constructor performs no I/O and opens no
connection. from_key is the convenience that builds one.
Fields
clientpropertyThe client this source uses, for a caller that wants to share it.__init__
__init__(self, client: httpx.Client, api_key: str | None = None, base_url: str = https://api.etherscan.io/v2/api, page_size: int = 1000) -> None
Bind the client and credentials. ZERO I/O happens here.
api_key=None is servable: the apikey param is omitted and
Etherscan's keyless tier applies. page_size is the balance
source's token-discovery page size; the history seam is paged by
the engine, which passes its own offset per call.
Parameters
clientrequired, httpx.ClientInjected httpx.Client. Nothing is opened here.
api_keyoptional, str | None, default NoneNone omits the apikey param entirely: the keyless tier, not an empty key.
base_urloptional, str, default 'https://api.etherscan.io/v2/api'Override to point at a proxy or a compatible endpoint.
page_sizeoptional, int, default 1000Token-discovery page size for balances.
from_key
from_key(cls, api_key: str | None = None, *, timeout_s: float = 10.0, base_url: str = https://api.etherscan.io/v2/api, page_size: int = 1000) -> EtherscanSource
Build a source owning its own httpx.Client.
The one place in this package that constructs a client from a
credential, which is why it lives in sources: an I/O domain.
A host that wants connection pooling, proxies, custom TLS or its
own retry policy passes the client to __init__ instead; this
classmethod is a default, never a requirement.
Parameters
api_keyoptional, str | None, default NoneOptional. One key covers every eip155:* chain.
timeout_soptional, float, default 10.0Applied to the client this builds for you.
base_urloptional, str, default 'https://api.etherscan.io/v2/api'Override to point at a proxy.
page_sizeoptional, int, default 1000Token-discovery page size for balances.
balances
balances(self, chain_id: str, address: str) -> list[BalanceRecord]
What address holds on chain_id now (SPEC §6.1).
Delegated verbatim to EtherscanV2.balances: native coin plus
every token the address has ever touched, deduplicated, with
undecodable rows skipped rather than guessed at. Raises
CaipParseError for a non-CAIP-2 chain before any request, and
SourceError for anything Etherscan refuses.
Parameters
chain_idrequired, strnone
addressrequired, strnone
Raises
fetch_txlist
fetch_txlist(self, chain_id: str, address: str, *, start_block: int, end_block: int, page: int, offset: int, sort: str) -> list[dict]
One page of raw history rows for exactly the window asked for.
The engine owns the window and the budget: it picks
start_block/end_block/page/sort, and a page shorter
than offset is how it learns the window drained. So this method
widens nothing, retries nothing and pages nothing: one request,
one answer.
Rows are returned RAW (list[dict]) because parsing belongs to
the decoder seam, which a host may replace. Raises SourceError
on any upstream refusal, which Auradefi.sync contains to this
one connection's report row rather than losing the whole tick.
Parameters
chain_idrequired, strCAIP-2 id; converted to Etherscan's numeric chainid.
addressrequired, strThe account whose history this page covers.
start_blockrequired, intInclusive lower bound the ENGINE chose.
end_blockrequired, intInclusive upper bound the ENGINE chose.
pagerequired, int1-based page within that window.
offsetrequired, intRows per page. A shorter page means the window drained.
sortrequired, strasc or desc. The engine anchors desc and walks live asc.
Possible values: "asc", "desc"