Skip to content

Support optional fetching of "AccountClassification" when making primary name lookups #1634

@lightwalker-eth

Description

@lightwalker-eth

This additional metadata adds latency and cost to calculate. It should only be calculated and returned if explicitly requested through an optional param.

Background info: Contracts were proposed to be added to the Contracts data files in ENSAwards that weren't actually contracts. An investigation revealed they were actually EOAs. ENSAwards already has a suite of unit tests that aim to protect against bad data. If the primary name lookup APIs in ENSApi made it easy to query this additional metadata, it could support improved data quality in ENSAwards. More broadly, I can see a number of other special use cases where returning this metadata could be helpful.

Note: The following spec was quickly generated using ChatGPT.

Spec: Add viem-based account classification (EOA-like vs Contract)

Goal

Implement a small utility to classify an on-chain account as EOA-like (no deployed runtime bytecode) or Contract (has runtime bytecode), using viem PublicClient.getBytecode() on the chain specified by chainId.


Requirements

1) Define AccountClassification enum

  • Create an enum named AccountClassification
  • It must have exactly two values:
    • EOALike
    • Contract

Example shape (TypeScript):

  • AccountClassification.EOALike
  • AccountClassification.Contract

2) Define classifyAccount function

Create an async function:

Name: classifyAccount
Inputs:

  1. account: object containing:
    • chainId: number
    • address: Address (from viem)
  2. clientsByChainId: Map<number, PublicClient> (from viem)

Output:

  • Promise<AccountClassification>

3) Error if chainId missing

  • If account.chainId is not present as a key in clientsByChainId, the function must throw an Error.
  • Error message should be explicit and include the missing chainId (e.g., "No PublicClient configured for chainId=XXXX").

4) Error if classification fails

  • If anything fails during classification (e.g., RPC error, invalid params, viem throws, unexpected response), the function must throw an Error.
  • The thrown error should:
    • Preserve the original error as the cause when possible (Node/TS supports new Error(msg, { cause })).
    • Include enough context to debug (at minimum: chainId and address).

No “UNKNOWN” / fallback return values—fail hard.


5) Use getBytecode to classify

  • Use the correct PublicClient from clientsByChainId for the given chainId.
  • Call publicClient.getBytecode({ address }).
  • Classification rules:
    • If bytecode is exactly '0x' (or undefined if viem returns that for empty code), return AccountClassification.EOALike.
    • Otherwise, return AccountClassification.Contract.

Acceptance Criteria

  • ✅ Enum exists with only EOALike and Contract.
  • classifyAccount(account, clientsByChainId) returns the expected enum value for:
    • An EOA address on that chain (returns EOALike)
    • A deployed contract address on that chain (returns Contract)
  • ✅ Missing chainId in the map throws an error.
  • ✅ Any RPC/viem failure during getBytecode throws an error with chain/address context.
  • ✅ Uses viem getBytecode via the chain-specific PublicClient.

Notes / Non-goals

  • This utility does not attempt to distinguish “true EOA” vs “self-destructed contract” / “counterfactual address”; it only classifies based on current deployed bytecode.
  • No caching required in this initial implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions