Skip to content

v1.0.6

Choose a tag to compare

@VaitaR VaitaR released this 18 Sep 13:02
· 8 commits to main since this release
85e143c

Added

  • Decoding is on the client surface. decode_input(calldata, address=...),
    decode_transactions(txs) and decode_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.decode functions or implicitly through iter_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 empty decoded_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
    topics list to four entries with nulls, and every such log decoded to nothing — on the
    keyless default provider, that was every log. decode_log_data now reads an absent slot
    as no topic, which fixes decode_logs, SmartContract.iter_events and 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_strategy chooses 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, and ProxyMetadata.source records 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.facets names the sources and
    SmartContract.missing_facets names any facet with no verified ABI, so a partial ABI
    cannot pass for a complete one. The MCP tools report both in notes.

  • 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_contract still sends its eth_call to
    the proxy address — only the ABI comes from the implementation.

  • Proxy metadata is read in both explorer dialects. Only Etherscan's Proxy /
    Implementation keys 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 (a ValueError subclass, exported from the package root) is
    raised by resolve_name/lookup_address when the scanner/network combination cannot
    serve ENS — where they previously raised a bare ValueError that callers could not
    distinguish from MethodNotDeclaredError. resolve_names/lookup_addresses now raise
    the same signal instead of silently returning {}. Catching ValueError keeps working;
    record-absent results remain None / omitted from batch dicts.

  • Provider labels are one spelling everywhere. Incompleteness errors and pagination
    contexts named a pool member scanner/version while last_provider said
    scanner/network; both now use the same label (ChainscanClient.provider_label), so an
    error and the sticky-routing report agree about who answered.

  • aioscan check lists credential files by asking ConfigurationManager for its actual
    candidates: a rebound config_dir is honoured (the listing used to guess cwd), 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 the registry/ package (data /
    views / resolution) behind a re-export facade, killing the circular import with
    config.py; BlockScout instance hosts, moralis_hex and 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 their StreamSpec rows (collect_for_aggregate);
    the response-dialect subsystem moved from network.py (794 → 582 lines) to
    response_dialects.py; CLI/MCP re-use chains_served_by / config_id_for_scanner
    instead of re-deriving the construction gates.