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

@thenamespace/avatar

v1.0.0

Published

TypeScript SDK for managing ENS avatar and header images with SIWE authentication

Readme

Namespace Ninja

Namespace SDK - ENS Images

npm version

Overview

The @thenamespace/avatar provides an easy-to-use client for managing ENS avatar and header images with SIWE authentication. With this SDK, developers can upload, update, and delete avatar and header images for ENS subnames.

Features

  • 🔐 SIWE Authentication - Secure Sign-In with Ethereum
  • 🌐 Direct Wallet Integration - Pass Viem, Ethers, or any wallet client directly - no adapters needed!
  • 📱 Framework Agnostic - Use with any frontend framework
  • 🎯 TypeScript First - Full type safety
  • Pre-registration Support - Upload before ENS registration
  • 📊 Progress Tracking - Real-time upload progress
  • 🛡️ File Validation - Size limits and format validation

Getting Started

Installation

npm install @thenamespace/avatar

Import the SDK

import { createAvatarClient } from "@thenamespace/avatar";

Initialize the Client

// 1) Initialize with domain (required)
const client = createAvatarClient({
  domain: "myapp.com", // Your website domain
});

// 2) Configure network
const client = createAvatarClient({
  domain: "myapp.com",
  network: "sepolia", // or "mainnet"
});

// 3) Initialize with provider (automatic signing)
const client = createAvatarClient({
  domain: "myapp.com",
  network: "mainnet",
  provider: walletProvider,
});

Usage

Automatic Flow (with Provider)

✨ NEW: Just pass your wallet client directly - no adapters needed!

With Viem

import { createAvatarClient } from "@thenamespace/avatar";
import { createWalletClient, http } from "viem";
import { mainnet } from "viem/chains";

const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
});

// Just pass the viem wallet client directly - no adapter needed!
const client = createAvatarClient({
  domain: "myapp.com",
  network: "mainnet",
  provider: walletClient, // Pass viem wallet client directly
});

// Upload avatar - SDK handles everything
const result = await client.uploadAvatar({
  subname: "myavatar.offchainsub.eth",
  file: avatarFile,
  onProgress: (progress) => console.log(`Upload: ${progress}%`),
});

console.log("Avatar uploaded:", result.url);

With Ethers.js

import { createAvatarClient } from "@thenamespace/avatar";
import { JsonRpcProvider, Wallet } from "ethers";

const provider = new JsonRpcProvider(RPC_URL);
const wallet = new Wallet(privateKey, provider);

// Just pass the ethers wallet directly - no adapter needed!
const client = createAvatarClient({
  domain: "myapp.com",
  network: "mainnet",
  provider: wallet, // Pass ethers wallet directly
});

// Upload works the same way
const result = await client.uploadAvatar({
  subname: "myavatar.offchainsub.eth",
  file: avatarFile,
});

With Wagmi (React)

import { createAvatarClient } from "@thenamespace/avatar";
import { useWalletClient } from "wagmi";

function MyComponent() {
  const { data: walletClient } = useWalletClient();

  const client = createAvatarClient({
    domain: "myapp.com",
    network: "mainnet",
    provider: walletClient, // Pass wagmi's wallet client directly
  });

  // Use the client...
}

Custom Provider (Advanced)

If you need to create a custom adapter:

const customProvider = {
  getAddress: () => "0x...",
  signMessage: (msg) => signatureFunction(msg),
  getChainId: () => 1,
};

const client = createAvatarClient({
  domain: "myapp.com",
  provider: customProvider,
});

Manual Flow (without Provider)

import { createAvatarClient } from "@thenamespace/avatar";

// Initialize without provider
const client = createAvatarClient({
  domain: "myapp.com", // Your website domain
  network: "mainnet",
});

// Get SIWE message (domain automatically used from initialization)
const siweResult = await client.getSIWEMessageForAvatar({
  address: "0x...",
});

// Sign message yourself
const signature = await wallet.signMessage(siweResult.message);

// Upload with signature
const result = await client.uploadAvatarWithSignature({
  subname: "myavatar.offchainsub.eth",
  file: avatarFile,
  message: siweResult.message,
  signature,
  address: "0x...",
});

API Reference

AvatarClient

Constructor

createAvatarClient(config?: AvatarSDKConfig): AvatarClient

Methods

Automatic Flow (requires provider)
  • uploadAvatar(options: UploadOptions): Promise<UploadResult>
  • uploadHeader(options: UploadOptions): Promise<UploadResult>
  • deleteAvatar(options: DeleteOptions): Promise<DeleteResult>
  • deleteHeader(options: DeleteOptions): Promise<DeleteResult>
Manual Flow
  • getSIWEMessageForAvatar(options: SIWEMessageOptions): Promise<SIWEMessageResult>
  • getSIWEMessageForHeader(options: SIWEMessageOptions): Promise<SIWEMessageResult>
  • uploadAvatarWithSignature(options: UploadWithSignatureOptions): Promise<UploadResult>
  • uploadHeaderWithSignature(options: UploadWithSignatureOptions): Promise<UploadResult>
  • deleteAvatarWithSignature(options: DeleteWithSignatureOptions): Promise<DeleteResult>
  • deleteHeaderWithSignature(options: DeleteWithSignatureOptions): Promise<DeleteResult>

Configuration

interface AvatarSDKConfig {
  domain: string; // Your website domain (required for SIWE authentication)
  apiUrl?: string; // API endpoint (defaults to production)
  network?: "mainnet" | "sepolia"; // Network (defaults to mainnet)
  provider?: WalletProvider; // Optional wallet provider
}

File Validation

  • Avatar: Max 2MB
  • Header: Max 5MB
  • Formats: JPEG, PNG, GIF, WebP

Error Handling

The SDK provides comprehensive error handling with specific error types:

import { AvatarSDKError, ErrorCodes } from "@thenamespace/avatar";

try {
  await client.uploadAvatar(options);
} catch (error) {
  if (error instanceof AvatarSDKError) {
    switch (error.code) {
      case ErrorCodes.FILE_TOO_LARGE:
        console.log("File is too large");
        break;
      case ErrorCodes.NOT_SUBNAME_OWNER:
        console.log("You do not own this ENS name");
        break;
      case ErrorCodes.INVALID_SIGNATURE:
        console.log("Invalid signature");
        break;
      // ... other error cases
    }
  }
}

Examples

Viem Integration

import { createWalletClient, createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
import { createAvatarClient } from "@thenamespace/avatar";

// Setup Viem
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(),
});

const walletClient = createWalletClient({
  account: "0x...",
  chain: mainnet,
  transport: http(),
});

// Create SDK
const client = createAvatarClient({
  domain: "myapp.com", // Your website domain
  network: "mainnet",
  provider: {
    getAddress: () => walletClient.account.address,
    signMessage: (msg) => walletClient.signMessage({ message: msg }),
    getChainId: () => walletClient.chain.id,
  },
});

// Upload avatar
const result = await client.uploadAvatar({
  subname: "myavatar.offchainsub.eth",
  file: avatarFile,
  onProgress: (progress) => console.log(`Upload: ${progress}%`),
});

Ethers.js Integration

import { BrowserProvider } from "ethers";
import { createAvatarClient } from "@thenamespace/avatar";

// Setup Ethers
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

// Create SDK
const client = createAvatarClient({
  domain: "myapp.com", // Your website domain
  network: "mainnet",
  provider: {
    getAddress: () => signer.getAddress(),
    signMessage: (msg) => signer.signMessage(msg),
    getChainId: async () => {
      const network = await provider.getNetwork();
      return Number(network.chainId);
    },
  },
});

// Upload header
const result = await client.uploadHeader({
  subname: "myavatar.offchainsub.eth",
  file: headerFile,
});

React Integration

import React, { useState } from "react";
import { createAvatarClient } from "@thenamespace/avatar";

export function AvatarUploader() {
  const [uploading, setUploading] = useState(false);
  const [progress, setProgress] = useState(0);
  const [result, setResult] = useState<string | null>(null);

  const handleUpload = async (file: File) => {
    setUploading(true);
    setProgress(0);

    try {
      const client = createAvatarClient({
        domain: "myapp.com", // Your website domain
        network: "mainnet",
        provider: window.ethereum, // Auto-detects provider
      });

      const result = await client.uploadAvatar({
        subname: "myavatar.offchainsub.eth",
        file,
        onProgress: setProgress,
      });

      setResult(result.url);
    } catch (error) {
      console.error("Upload failed:", error);
    } finally {
      setUploading(false);
    }
  };

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        onChange={(e) => e.target.files?.[0] && handleUpload(e.target.files[0])}
      />
      {uploading && <div>Uploading... {progress}%</div>}
      {result && <img src={result} alt="Avatar" />}
    </div>
  );
}

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Run npm run test to ensure all tests pass
  6. Submit a pull request

License

This project is licensed under the MIT License.

Links