npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@1001-digital/proxies

v0.1.1

Published

Ethereum proxy pattern detection primitives — ERC-2535 diamonds, EIP-1967, EIP-1167, beacon, Safe, EIP-1822, EIP-897. Detect, enrich, compose.

Downloads

233

Readme

@1001-digital/proxies

Ethereum proxy pattern detection primitives for TypeScript — ERC-2535 diamonds, EIP-1967, EIP-1167, beacon, Safe, EIP-1822, EIP-897.

Given a contract address, resolve where the real code lives: one implementation (plain proxy) or N facets (diamond). Uniform detect → enrich → compose pipeline across all patterns.

Narrow on purpose: this package knows proxy conventions, selectors, and ABIs. Anything richer — Sourcify, NatSpec, repository metadata — is the consumer's concern. Bring your own enricher.

Who this is for

Use this package when you need a small, composable proxy-resolution layer in:

  • explorer and indexer backends
  • contract introspection CLIs
  • ABI fetchers and decoding pipelines
  • developer tooling that needs implementation or facet discovery without owning proxy-specific logic

What this package is not

This package intentionally does not try to be:

  • a contract metadata SDK
  • a verification or source-code retrieval client
  • a recursive proxy resolver
  • an upgrade history tracker
  • an opinionated registry or indexing layer

If you need sources, NatSpec, repository metadata, verification status, or custom per-target fields, use detect() and own enrichment yourself.

Install

pnpm add @1001-digital/proxies

Usage

Detect + fetch a proxy

import { createProxies } from '@1001-digital/proxies'

const proxies = createProxies()

const result = await proxies.fetch(
  'https://eth.llamarpc.com',
  '0xProxyAddress…',
)

if (result) {
  console.log(result.pattern)        // 'eip-1967' | 'eip-2535-diamond' | …
  console.log(result.targets)        // [{ address, selectors?, abi? }, …]
  console.log(result.compositeAbi)   // undefined — no enricher configured
}

Enrich with Sourcify (or anything else)

The enricher returns each target's ABI. Anything richer (sources, bytecode, documentation, …) is the consumer's concern — see Detect and do your own enrichment below.

import { createProxies } from '@1001-digital/proxies'

async function sourcifyAbi(address: string) {
  const res = await fetch(
    `https://sourcify.dev/server/v2/contract/1/${address}?fields=abi`,
  )
  if (!res.ok) return null
  const { abi } = await res.json()
  return { abi }
}

const proxies = createProxies({ enrich: sourcifyAbi })

const result = await proxies.fetch(
  'https://eth.llamarpc.com',
  '0xProxyAddress…',
)

if (result) {
  console.log(result.targets[0].abi) // ABI (filtered to selectors for diamonds, full for plain proxies)
  console.log(result.compositeAbi)   // all target ABIs deduped by selector
}

Detect and do your own enrichment

For richer per-target metadata, use detect and own the enrichment step end-to-end. filterAbiBySelectors and buildCompositeAbi remain useful primitives.

import {
  createProxies,
  filterAbiBySelectors,
  buildCompositeAbi,
} from '@1001-digital/proxies'

const proxies = createProxies()
const raw = await proxies.detect(rpc, address)

if (raw) {
  const enriched = await Promise.all(raw.targets.map(async t => {
    const src = await mySource(t.address)
    return {
      ...t,
      abi: src?.abi && t.selectors
        ? filterAbiBySelectors(src.abi, t.selectors)
        : src?.abi,
      metadata: src?.metadata,
    }
  }))

  const compositeAbi = buildCompositeAbi(
    enriched.map(t => t.abi).filter((a): a is unknown[] => !!a),
  )
}

Standalone primitives

All low-level utilities are exported directly — use them without the factory:

import {
  detectProxy,
  detectDiamond,
  detectEip1967,
  decodeFacets,
  computeSelector,
  canonicalSignature,
  filterAbiBySelectors,
  buildCompositeAbi,
  enrichTargets,
} from '@1001-digital/proxies'

decodeFacets('0x…')                            // parse a facets() return value
computeSelector('transfer(address,uint256)')   // '0xa9059cbb'
canonicalSignature({ type: 'function', name: 'transfer', inputs: [/*…*/] })

API

createProxies(config?)

Creates a proxies client.

Config options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | enrich | TargetEnricher | — | Default per-target enricher. Called with each target address; return { abi? } or null. Errors are swallowed per-target. | | fetch | typeof fetch | globalThis.fetch | Custom fetch function. |

Returns a ProxiesClient with:

  • detect(rpc, address)Promise<RawProxy | null>. On-chain probe only.
  • fetch(rpc, address, options?)Promise<Proxy | null>. Detect, enrich, and compose.
    • options.enrich — per-call enricher (overrides config-level)
    • options.enrich = false — skip enrichment for this call

Detection

  • detectProxy(rpc, address, fetchFn) — tries all patterns in priority order (diamond → 1967 → 1967-beacon → 1822 → 1167 → safe → 897), returns the first match.
  • detectDiamond — ERC-165 probe, then facets() fallback.
  • detectEip1967 — reads impl slot 0x3608…3bc3; optionally admin slot.
  • detectEip1967Beacon — reads beacon slot 0xa3f0…750d, then implementation() on the beacon.
  • detectEip1822 — reads PROXIABLE slot 0xc5f1…f8e2.
  • detectEip1167eth_getCode, matches minimal proxy bytecode (363d3d…bf3).
  • detectGnosisSafe — reads storage slot 0.
  • detectEip897 — calls implementation() as a last resort.

Each detector returns null if the pattern doesn't match; otherwise a RawProxy.

Detection tradeoffs

  • First match wins — detector order is intentional. If a contract could satisfy multiple heuristics, detectProxy returns the first supported pattern in priority order.
  • Single-hop resolution — if a resolved implementation is itself a proxy, detection stops there. Beacon remains supported as a defined two-step pattern.
  • Error isolation — one failed RPC probe does not poison the full detection pipeline.

Composition

  • enrichTargets(targets, enricher | null) — applies the enricher to each target; ABIs are filtered to live selectors for diamonds, passed through for plain proxies.
  • buildCompositeAbi(abis) — pure; dedupes functions/events/errors across ABIs (first-wins).

Utilities

  • decodeFacets(hex) — pure; decodes (address, bytes4[])[] loupe return.
  • computeSelector(signature) — pure; keccak256-based 4-byte selector.
  • canonicalSignature(abiEntry) — pure; normalizes tuples/arrays into a signature string.
  • filterAbiBySelectors(abi, selectors) — pure; keeps non-functions, filters functions by selector.
  • ethCall, ethGetStorageAt, ethGetCode — minimal JSON-RPC helpers.

Constants

SUPPORTS_INTERFACE_SELECTOR      // '0x01ffc9a7'
DIAMOND_LOUPE_INTERFACE_ID       // '0x48e2b093'
FACETS_SELECTOR                  // '0x7a0ed627'
IMPLEMENTATION_SELECTOR          // '0x5c60da1b'
EIP1967_IMPL_SLOT
EIP1967_BEACON_SLOT
EIP1967_ADMIN_SLOT
EIP1822_PROXIABLE_SLOT
EIP1167_BYTECODE_PREFIX
EIP1167_BYTECODE_SUFFIX
ZERO_ADDRESS

Errors

  • ProxiesError — base class.
  • ProxiesDecodeError — malformed facets() return.
  • ProxiesFetchError — JSON-RPC transport error.

The client's detect and fetch methods swallow RPC-layer errors and return null; only decodeFacets (called directly) can throw ProxiesDecodeError on malformed input.

Shapes

ProxyPattern

type ProxyPattern =
  | 'eip-2535-diamond'
  | 'eip-1967'
  | 'eip-1967-beacon'
  | 'eip-1822'
  | 'eip-1167'
  | 'gnosis-safe'
  | 'eip-897'

ResolvedTarget

{
  address: string
  // undefined = all selectors route here (plain proxy)
  // defined = diamond facet selector scope
  selectors?: string[]
}

RawProxy

{
  pattern: ProxyPattern
  targets: ResolvedTarget[]   // 1 entry except for diamonds
  beacon?: string             // only for eip-1967-beacon
  admin?: string              // only for eip-1967 when admin slot is set
}

EnrichedTarget

{
  address: string
  selectors?: string[]
  abi?: unknown[]
}

Proxy

{
  pattern: ProxyPattern
  targets: EnrichedTarget[]
  beacon?: string
  admin?: string
  compositeAbi?: unknown[]   // deduped by selector
}

TargetEnrichment

{ abi?: unknown[] }

TargetEnricher

type TargetEnricher = (address: string) => Promise<TargetEnrichment | null>

Design notes

  • Narrow scope — detects patterns and composes ABIs; no opinion on documentation formats or richer per-target metadata.
  • Dependency-injected enrichment — the factory wires I/O when you ask; the primitives work offline.
  • First-wins ABI dedup — pass the most authoritative ABI first (e.g. main contract → impl, or main diamond → facets).
  • Single-hop resolution — if a resolved implementation is itself a proxy, detectProxy does not recurse. Beacon stays supported as a defined two-step pattern.
  • Minimal runtime deps — only @noble/hashes for keccak256.

Example consumers

  • An explorer backend can call detect() to resolve a proxy and then enrich targets from Sourcify or an internal cache.
  • A CLI can call fetch() to print the detected pattern, target addresses, and a composite ABI for downstream decoding.
  • An indexing pipeline can treat detectProxy → enrichTargets → buildCompositeAbi as a reusable normalization step before storage.

License

MIT