Repository navigation
v1.0.6
Added
-
Decoding is on the client surface.
decode_input(calldata, address=...),
decode_transactions(txs)anddecode_logs(logs)resolve the ABI that decodes traffic
sent to an address — proxy implementation, or every facet of a diamond — and apply it to
records the caller already holds. The decoders were reachable only as
aiochainscan.decodefunctions or implicitly throughiter_transactions(abi=...), so
the ABI resolution in front of them was work every caller wrote again. Records whose ABI
cannot be fetched keep every field with an emptydecoded_func; the caller's lists are
never mutated, and the ABI is fetched once per destination address. -
Null topic padding no longer blocks event decoding. BlockScout V1 pads a log's
topicslist to four entries with nulls, and every such log decoded to nothing — on the
keyless default provider, that was every log.decode_log_datanow reads an absent slot
as no topic, which fixesdecode_logs,SmartContract.iter_eventsand the streaming
decode paths alike. -
Proxy resolution is reachable from the name agents actually use.
get_contract_abi(address, follow_proxy=True)resolves a proxy to its implementation
before fetching the ABI; without the flag it still returns the ABI the explorer stores
for that address, which for a proxy declares none of the functions its traffic calls.
get_contract()/SmartContract.from_address()already resolved and now say so. -
proxy_strategychooses where an implementation is looked for:'metadata'
(default, explorer only, no extra request),'chain'(storage slots and the EIP-2535
loupe only) or'auto'(metadata first, chain as fallback, up to five extra requests).
The chain path reads the EIP-1967, EIP-1822, legacy zeppelinos and EIP-1967 beacon slots
in that order — no single slot suffices, as USDC keeps its implementation in the legacy
slot while stkAAVE answers at EIP-1967. Where explorer and chain disagree the chain
wins, andProxyMetadata.sourcerecords which answered. -
Diamonds (EIP-2535) decode. A diamond's ABI is assembled from every facet the
facets()loupe reports (or every address BlockScout lists in
ImplementationAddresses);SmartContract.facetsnames the sources and
SmartContract.missing_facetsnames any facet with no verified ABI, so a partial ABI
cannot pass for a complete one. The MCP tools report both innotes. -
Method.PROXY_GET_STORAGE_AT/client.eth_get_storage_at(address, position, tag),
declared by Etherscan v2, BlockScout v1 and NodeReal.
Changed
-
Every ABI-consuming MCP tool resolves proxies first (
get_contract_abi,
read_contract,get_transaction_info's input decode), where they previously fetched
the proxy's own ABI and decoded nothing.read_contractstill sends itseth_callto
the proxy address — only the ABI comes from the implementation. -
Proxy metadata is read in both explorer dialects. Only Etherscan's
Proxy/
Implementationkeys were consulted, so every BlockScout proxy (IsProxy/
ImplementationAddress/ImplementationAddresses) read as a plain contract — on the
keyless default MCP scanner, that was every proxy. -
One ENS unavailability signal, honoured by the batch path. The new
ENSScannerUnavailableError(aValueErrorsubclass, exported from the package root) is
raised byresolve_name/lookup_addresswhen the scanner/network combination cannot
serve ENS — where they previously raised a bareValueErrorthat callers could not
distinguish fromMethodNotDeclaredError.resolve_names/lookup_addressesnow raise
the same signal instead of silently returning{}. CatchingValueErrorkeeps working;
record-absent results remainNone/ omitted from batch dicts. -
Provider labels are one spelling everywhere. Incompleteness errors and pagination
contexts named a pool memberscanner/versionwhilelast_providersaid
scanner/network; both now use the same label (ChainscanClient.provider_label), so an
error and the sticky-routing report agree about who answered. -
aioscan checklists credential files by askingConfigurationManagerfor its actual
candidates: a reboundconfig_diris honoured (the listing used to guesscwd), and the
precedence caption matches the real load order (earlier entries override later — the old
caption said the opposite).
Internal
- Architecture pass 2026-09-13 (
docs/architecture/2026-09-13-review.md, C23–C30, merge
5abd365):chain_registry.py(1,869 lines) split into theregistry/package (data /
views / resolution) behind a re-export facade, killing the circular import with
config.py; BlockScout instance hosts,moralis_hexand the Etherscan network surface
are single-sourced (the guard tests policing their duplicates became derivation pins);
network-name dialects ('eth'/'ethereum'/'main') are declared on the scanner classes
(Scanner.dialect_network) instead of hardcoded in the registry; the pool's three
parallel failover walks collapsed into one engine with the cursor-stamp vocabulary
declared beside the pagination engine's provider-cursor check;
get_all_*aggregators route through theirStreamSpecrows (collect_for_aggregate);
the response-dialect subsystem moved fromnetwork.py(794 → 582 lines) to
response_dialects.py; CLI/MCP re-usechains_served_by/config_id_for_scanner
instead of re-deriving the construction gates.