vike

Example 1 of 32

Crypto address lookup: labels and entity for an ETH wallet

Put a name to an Ethereum address: its label, the sources behind it and its ENS name, in three calls.

Give it an address and it answers three questions with three routes: what is it (/labels/batch with a one-element list), who says so (/labels/{address}/provenance), and does it have a name (/wallets/{address}/ens).

Run
export VIKE_API_KEY=...
python oc-whose-wallet.py                  # the default address is vitalik.eth's
python oc-whose-wallet.py 0x28C6c06298d514Db089934071355E5743bf21d60
python oc-whose-wallet.py --dry-run        # prints the requests it would make, calls nothing
oc-whose-wallet.py
"""Put a name to an Ethereum address: label, who said so, and its ENS name.

Run:  VIKE_API_KEY=... python oc-whose-wallet.py [0xADDRESS]    (--dry-run lists the requests and makes none)
"""
import os
import sys
from urllib.parse import urlencode

NAME = "oc-whose-wallet"
BASE = "https://data.vike.io"
DRY = "--dry-run" in sys.argv
ARGS = [a for a in sys.argv[1:] if not a.startswith("--")]
WALLET = ARGS[0] if ARGS else "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"


def fail(why):
    sys.exit(f"{NAME}: {why}")


def call(path, method="GET", **params):
    url = BASE + path + ("?" + urlencode(params) if params else "")
    if DRY:
        print(method, url)
        return None
    key = os.environ.get("VIKE_API_KEY") or fail("set VIKE_API_KEY")
    import requests

    r = requests.request(method, url, headers={"X-API-Key": key}, timeout=60)
    if r.status_code != 200:
        fail(f"{method} {path} answered HTTP {r.status_code}: {r.text[:200]}")
    return r.json()


def main():
    # A POST with no body; one address is a one-element list. An address that is not valid comes back as [].
    batch = call("/v1/onchain/labels/batch", method="POST", addresses=WALLET)
    # Who said it: the consensus plus every source's own claim. A thin list can mean a source is rebuilding.
    prov = call(f"/v1/onchain/labels/{WALLET}/provenance")
    # Reverse ENS. source says which path answered: local (our store) or rpc (a fresh lookup).
    ens = call(f"/v1/onchain/wallets/{WALLET}/ens")
    if DRY:
        return
    if not batch:
        fail(f"{WALLET} is not a valid address")
    row = batch[0]
    print(f"{WALLET}")
    if row["found"]:
        print(f"  label    {row['entity']} ({row['wallet_type']}), confidence {row['confidence']}")
        print(f"  30 days  ${row['volume_30d_usd']:,.0f} volume, {row['tx_30d']} txs, risk flag {row['risk_flag'] or 'none'}")
    else:
        print("  label    none on record")
    claims = "; ".join(s["label"] for s in prov.get("sources", []))
    print(f"  sources  {prov.get('source_count', 0)}: {claims or 'none name it'}")
    name = ens["ens"]
    if name:
        print(f"  ens      {name}")
    elif ens["source"] == "rpc":
        print("  ens      none found, but an rpc miss may only mean the resolvers were unreachable: retry before concluding")
    else:
        print("  ens      no name set")


if __name__ == "__main__":
    try:
        main()
    except Exception as e:  # a shape the example does not expect fails by name, not by traceback
        fail(f"{type(e).__name__}: {e}")

Read it this way

  • A malformed address comes back as []. /labels/batch answers a batch whose every address is malformed with a 200 and an empty array, so the script checks the length as well as the status.
  • found: false is not a failure. The address has no label row; the script prints "none on record".
  • risk_flag is the empty string when clean, so the script tests truthiness.
  • Sources speak for themselves. Each claim is printed in its source's own words, and they can disagree: vitalik.eth drew "Vitalik Buterin" and "sushiswap" on 2026-10-19. The provenance route also returns fewer rows rather than failing when a label source is mid-rebuild, so re-query a thin list before concluding.

An ENS miss over rpc is not proof of no name

source says which path answered. local with ens: null is a recorded fact: the address was resolved and has no name. rpc with ens: null may only mean every resolver was unreachable, and it still comes back as a 200. The script prints that case differently; retry before treating the address as unnamed.

On this page