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

@nxt3d/ecsjs

v0.2.4-beta

Published

JavaScript library for ECS V2 - Ethereum Credential Service registry and resolver

Downloads

41

Readme

ecsjs - Ethereum Credential Service V2

npm version TypeScript License: MIT

Version: 0.2.4-beta
Status: Beta - Deployed on Sepolia

JavaScript/TypeScript library for interacting with ECS V2 (Ethereum Credential Service), a decentralized registry for "known" credential resolvers. ECS V2 is fully compatible with the ENS Hooks standard, enabling ENS names to securely resolve credentials from trusted resolvers.

Installation

npm install @nxt3d/[email protected]

Version: 0.2.4-beta - View on NPM
Important: ECS V1 is deprecated and incompatible with V2.
Note: This package includes viem as a dependency, so you don't need to install it separately.

What is ECS V2?

ECS V2 is a simplified, decentralized registry that maps labels (e.g., name-stars) to standard ENS resolvers. These resolvers serve verifiable credential data, either onchain or offchain (via CCIP-Read).

Goals of V2

  • Simplicity: The complex multi-level registry has been replaced with a flat, single-label registry. Labels (e.g., name-stars) map directly to resolvers.
  • Standard Resolvers: Credential resolvers are now just standard ENSIP-10 (Extended Resolver) contracts. This means any existing ENS tooling can interact with them.
  • Flexible Data: Credential providers can define their own schema and keys. There's no forced structure for credential data.
  • Hooks Integration: ECS serves as the registry for Hooks. Hooks in ENS text records can reference ECS resolvers to fetch trusted data.
  • Resolver Trust: The registry tracks resolverUpdated timestamps, allowing clients to enforce security policies based on resolver age.

Usage Flow with Hooks

Hooks enable ENS names to redirect queries to known resolvers.

  1. User sets a text record on their ENS name (e.g., maria.eth) containing a Hook:

    hook("text(bytes32,string)", 0xc8028D202838FF7D14835c75906A07839837C160)
  2. Client reads this record and extracts the resolver address.

  3. Client calls getResolverInfo(resolverAddress) on the ECS Registry to:

    • Find its registered label (e.g., name-stars)
    • Check the resolverUpdated timestamp to verify resolver stability
    • Read the review field for admin-assigned ratings or certifications
    • Make a trust decision based on resolver age and review status
  4. Client constructs the service name name-stars.ecs.eth (optional, for provenance).

  5. Client queries the resolver directly: text(node, "credential-key").

    • Note: Single-label resolvers ignore the node parameter, so any value (including 0x0) works.
  6. Resolver returns the verified credential data.

This creates a trusted link to the record, where maria.eth doesn't store the record herself; instead, the record can be resolved against a "known" trusted resolver.

Quick Start

import { 
  createECSClient, 
  sepolia,
  getResolverInfo, 
  resolveCredential 
} from '@nxt3d/ecsjs'

const client = createECSClient({
  chain: sepolia,
  rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY'
})

// User has hook pointing to resolver
const resolverAddress = '0xc8028D202838FF7D14835c75906A07839837C160'
const credentialKey = 'eth.ecs.name-stars.starts:vitalik.eth'

// Get label and resolve credential in one call
const credential = await resolveCredential(client, resolverAddress, credentialKey)
// Returns: "100"

// Or get resolver info
const { label, resolverUpdated, review } = await getResolverInfo(client, resolverAddress)
// Returns: { label: "name-stars", resolverUpdated: 1764948384n, review: "" }

// You can also use viem's ENS functions directly
const ensName = `${label}.ecs.eth`
const textValue = await client.getEnsText({
  name: ensName,
  key: credentialKey
})
// Returns: "100"

Resolver Trust and Freshness

ECS strictly enforces a one-to-one relationship between labels and resolvers. While label owners can change resolvers (necessary for upgrades), this introduces a security concern. The registry tracks resolverUpdated timestamps, allowing clients to enforce security policies based on resolver age.

Security-conscious clients can require resolvers to be established (e.g., 90+ days old) before trusting them. Recent resolver changes may indicate compromise, untested deployments, or migrations requiring review.

import { getResolverInfo, getResolverAge } from '@nxt3d/ecsjs'

const { label, resolverUpdated, review } = await getResolverInfo(client, resolverAddress)
const resolverAge = getResolverAge(resolverUpdated)
const ageInDays = Math.floor(resolverAge / 86400)

if (ageInDays < 90) { // 90 days for high security
  console.warn(`⚠️ Resolver for "${label}" changed ${ageInDays} days ago`)
  // Reject or require security review
}

// Check admin review status
if (review && review !== "verified") {
  console.warn(`⚠️ Resolver "${label}" review status: ${review}`)
}

API Reference

createECSClient(config)

Creates a Viem public client configured for ECS.

import { createECSClient, sepolia } from '@nxt3d/ecsjs'

const client = createECSClient({
  chain: sepolia,
  rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY'
})

Parameters:

  • config.chain: Chain to connect to (sepolia or mainnet)
  • config.rpcUrl: RPC URL for the chain

Returns: Viem PublicClient


getResolverInfo(client, resolverAddress)

Get information about a resolver from its address.

const { label, resolverUpdated, review } = await getResolverInfo(
  client,
  '0xc8028D202838FF7D14835c75906A07839837C160'
)

Parameters:

  • client: Viem public client
  • resolverAddress: The resolver address

Returns: Promise<{ label: string, resolverUpdated: bigint, review: string }>

  • label: The registered label (e.g., "name-stars")
  • resolverUpdated: Timestamp when the resolver was last updated
  • review: Admin-assigned review status or certification

resolveCredential(client, resolverAddress, credentialKey)

Resolve a credential from a known resolver.

const credential = await resolveCredential(
  client,
  '0x9773397bd9366D80dAE708CA4C4413Abf88B3DAa',
  'eth.ecs.name-stars.starts:vitalik.eth'
)

Parameters:

  • client: Viem public client
  • resolverAddress: The resolver address
  • credentialKey: The credential key to resolve

Returns: Promise<string | null>


getRegistryAddress(chainId)

Get the ECS Registry address for a given chain.

import { getRegistryAddress } from '@nxt3d/ecsjs'

const registryAddress = getRegistryAddress(11155111) // Sepolia
// Returns: "0xb09C149664773bFA88B72FA41437AdADcB8bF5B4"

Parameters:

  • chainId: The chain ID (e.g., 11155111 for Sepolia)

Returns: string - The registry address


getResolverAge(resolverUpdated)

Calculate the age of a resolver in seconds (helper for security checks).

import { getResolverAge } from '@nxt3d/ecsjs'

const { resolverUpdated } = await getResolverInfo(client, resolverAddress)
const ageInSeconds = getResolverAge(resolverUpdated)
const ageInDays = Math.floor(ageInSeconds / 86400)
console.log(`Resolver is ${ageInDays} days old`)

Parameters:

  • resolverUpdated: The resolverUpdated timestamp (bigint)

Returns: number - Age in seconds

Deployments

Sepolia Testnet

Version: 0.2.4-beta
Date: December 7, 2025
Network: Sepolia (Chain ID: 11155111)
Status: ✅ Live and operational (Deployment 01 - Resolver Review System)

Deployed Contracts

| Contract | Address | Verified | |----------|---------|----------| | ECS Registry | 0xb09C149664773bFA88B72FA41437AdADcB8bF5B4 | ✅ View | | ECS Registrar | 0xD1399C6879EA5A92eB25ee8A0512c7a4fC0DDc6b | ✅ View | | Credential Resolver (Implementation) | 0x9eC339D221dB86e5bcfB12B7861b3F8e41a5D5d9 | ✅ View | | Credential Resolver Factory | 0x9b2d7A50bb15F5147c2cf46f05FBfD0E931AB77A | ✅ View | | Credential Resolver (Clone - name-stars) | 0xc8028D202838FF7D14835c75906A07839837C160 | View |

New in v0.2.1: Resolver review system for admin trust ratings + OwnableUpgradeable pattern for clones

Configuration

  • Root Name: ecs.eth
  • Root Node: 0xe436ba58406c69a63a9611a11eb52314c5c17ba9eaaa7dab8506fe8849517286
  • Deployer: 0xF8e03bd4436371E0e2F7C02E529b2172fe72b4EF
  • Registrar Pricing: ~0.001 ETH/year (32000 wei/second)
  • Min Commitment Age: 60 seconds

Registered Labels

name-stars.ecs.eth
  • Status: ✅ Registered
  • Owner: 0xF8e03bd4436371E0e2F7C02E529b2172fe72b4EF
  • Resolver: 0xc8028D202838FF7D14835c75906A07839837C160 (minimal clone)
  • Expires: December 7, 2026

Credential Records:

  • Key: eth.ecs.name-stars.starts:vitalik.eth
  • Text Value: "100"
  • Data Value: 100 (uint256)

Examples

Basic Credential Resolution

import { createECSClient, sepolia, resolveCredential } from '@nxt3d/ecsjs'

const client = createECSClient({
  chain: sepolia,
  rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY'
})

// Resolve credential directly
const credential = await resolveCredential(
  client,
  '0xc8028D202838FF7D14835c75906A07839837C160',
  'eth.ecs.name-stars.starts:vitalik.eth'
)

console.log(`vitalik.eth has ${credential} stars`) // "vitalik.eth has 100 stars"

With Security Checks

import { 
  createECSClient, 
  sepolia, 
  getResolverInfo,
  getResolverAge,
  resolveCredential 
} from '@nxt3d/ecsjs'

const client = createECSClient({
  chain: sepolia,
  rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY'
})

const resolverAddress = '0xc8028D202838FF7D14835c75906A07839837C160'

// Check resolver info first
const { label, resolverUpdated, review } = await getResolverInfo(client, resolverAddress)
const ageInDays = Math.floor(getResolverAge(resolverUpdated) / 86400)

console.log(`Resolver: ${label}.ecs.eth`)
console.log(`Age: ${ageInDays} days`)
console.log(`Review: ${review || 'None'}`)

// Enforce security policy
if (ageInDays < 90) {
  throw new Error(`Resolver too new: ${ageInDays} days (require 90+)`)
}

// Check review status
if (review && review !== "verified") {
  console.warn(`⚠️ Review status: ${review}`)
}

// Proceed with resolution
const credential = await resolveCredential(
  client,
  resolverAddress,
  'eth.ecs.name-stars.starts:vitalik.eth'
)

console.log(`Credential: ${credential}`)

Using Viem Directly

Since ECS V2 uses standard ENS resolvers, you can also use Viem's ENS functions directly:

import { createECSClient, sepolia, getResolverInfo } from '@nxt3d/ecsjs'

const client = createECSClient({
  chain: sepolia,
  rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY'
})

// Get the label from resolver address
const { label } = await getResolverInfo(
  client,
  '0xc8028D202838FF7D14835c75906A07839837C160'
)

// Use Viem's getEnsText directly
const ensName = `${label}.ecs.eth`
const textValue = await client.getEnsText({
  name: ensName,
  key: 'eth.ecs.name-stars.starts:vitalik.eth'
})

console.log(textValue) // "100"

Development

Building

npm run build

Testing

npm test               # Run all tests
npm run test:watch     # Watch mode
npm run test:coverage  # Coverage report

Linting

npm run lint
npm run lint:fix

Migration from V1

ECS V2 is a complete rewrite with a different architecture. Key differences:

V1 (0.1.x) - Deprecated

  • Complex multi-level registry (name.coinType.addr.ecs.eth)
  • Custom credential resolution
  • Multi-currency support via coinType
  • Credential-first design

V2 (0.2.4-beta) - Current

  • Simple flat registry (label.ecs.eth)
  • Standard ENS resolvers (ENSIP-10)
  • ENS Hooks integration
  • Resolver-first design with trust tracking

Migration is not automatic. V2 requires new resolver deployments and a different integration approach. If you're using V1, please reach out for migration guidance.

TypeScript Support

This library is written in TypeScript and includes full type definitions. All functions are fully typed for the best developer experience.

import type { PublicClient, Chain } from '@nxt3d/ecsjs'

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see the LICENSE file for details.

Links


Note: ECS V2 is currently in beta on Ethereum Sepolia testnet. The library has been tested with real deployments and works correctly. Use at your own risk in production environments.