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

eip6963

v0.1.2

Published

Dependency-free EVM wallet connector using native browser APIs (EIP-6963 / EIP-1193). Vanilla core + optional React hooks.

Readme

eip6963

A dependency-free EVM wallet connector built on native browser APIs.

  • No third-party libraries. Uses EIP-6963 for wallet discovery and EIP-1193 for the provider. No ethers, no viem, no wagmi bundled.
  • Vanilla core + React layer. The framework-agnostic core is a tiny subscribable store; the React hooks are a thin wrapper over it via useSyncExternalStore.
  • Just connectivity. Discover injected wallets, connect/disconnect, switch chains, track account/chain changes, and auto-reconnect on reload. It hands you the raw EIP-1193 provider — plug it into ethers, viem, or anything else.
  • TypeScript-first, SSR-safe, ESM + CJS.

Install

npm install eip6963

React is an optional peer dependency — only needed if you import eip6963/react.

React

import {
  WalletProvider,
  useWallet,
  useProviders,
} from 'eip6963/react';

function Root() {
  return (
    <WalletProvider>
      <Connect />
    </WalletProvider>
  );
}

function Connect() {
  const providers = useProviders();
  const { account, chainId, status, connect, disconnect, switchChain } =
    useWallet();

  if (account) {
    return (
      <div>
        <p>{account} on chain {chainId}</p>
        <button onClick={() => switchChain(1)}>Switch to mainnet</button>
        <button onClick={disconnect}>Disconnect</button>
      </div>
    );
  }

  return (
    <div>
      {providers.map((p) => (
        <button key={p.info.uuid} onClick={() => connect(p)}>
          <img src={p.info.icon} width={20} alt="" /> {p.info.name}
        </button>
      ))}
      {status === 'connecting' && <span>Connecting…</span>}
    </div>
  );
}

Hooks

| Hook | Returns | | --- | --- | | useWallet() | Full state + connect / disconnect / switchChain + isConnected / isConnecting | | useProviders() | Eip6963ProviderDetail[] discovered so far | | useAccount() | Active address or null | | useChainId() | Active chain id (decimal) or null | | useWalletStatus() | { status, isConnected, isConnecting } | | useConnect() / useDisconnect() / useSwitchChain() | The individual actions (stable refs) | | useConnector() | The raw Connector instance |

<WalletProvider> accepts autoConnect (default true), storageKey, and a custom storage.

Vanilla

The React layer is optional — the core works anywhere:

import { createConnector } from 'eip6963';

const connector = createConnector();

connector.subscribe(() => {
  const { providers, account, chainId, status } = connector.getState();
  // re-render your UI
});

// connect by provider detail or by stable rdns
await connector.connect('io.metamask');

await connector.switchChain(1);
connector.disconnect();

// when you're done
connector.destroy();

Using the provider with ethers / viem

The connector deliberately doesn't bundle a signing library — it exposes the raw EIP-1193 provider so you can use whatever you like:

import { ethers } from 'ethers';

const { activeProvider } = connector.getState();
if (!activeProvider) throw new Error('No wallet connected');

const browserProvider = new ethers.BrowserProvider(activeProvider.provider);
const signer = await browserProvider.getSigner();
import { createWalletClient, custom } from 'viem';

const { activeProvider } = connector.getState();
if (!activeProvider) throw new Error('No wallet connected');

const client = createWalletClient({
  transport: custom(activeProvider.provider),
});

No cast needed. activeProvider.provider is a standard EIP-1193 provider and is structurally assignable to ethers' Eip1193Provider and viem's custom() transport exactly as shown above — you do not need as any or as never. If your editor flags a type error here, it's almost always a stale TS server (restart it: in VS Code, "TypeScript: Restart TS Server"). Avoid reaching for as never to silence it — that turns off type checking for the whole expression and will hide real mistakes later.

Switching chains (and adding unknown ones)

switchChain(chainId) calls wallet_switchEthereumChain. If the wallet doesn't recognize the chain it rejects with EIP-1193 code 4902 — pass EIP-3085 add-chain params as the second argument and the connector will add the chain via wallet_addEthereumChain and retry the switch for you:

await switchChain(7000, {
  chainName: 'ZetaChain Mainnet',
  nativeCurrency: { name: 'ZETA', symbol: 'ZETA', decimals: 18 },
  rpcUrls: ['https://zetachain-evm.blockpi.network/v1/rpc/public'],
  blockExplorerUrls: ['https://zetascan.com'],
});

Without the second argument, an unknown chain just throws — see error handling below.

Error handling

Failed connect / switchChain calls reject with a WalletError that preserves the provider's code (e.g. 4902 unknown chain, 4001 user rejected, -32002 request already pending) and data, so you can branch on it:

import { WalletError } from 'eip6963';

try {
  await switchChain(7000);
} catch (err) {
  if (err instanceof WalletError && err.code === 4902) {
    // chain not in the wallet — prompt to add it, or call switchChain with params
  }
}

The same WalletError is also exposed as state.error (and useWallet().error) after a failed attempt.

How auto-reconnect works

On connect, the wallet's uuid, rdns, name, and account are persisted (by default to localStorage). On the next load the connector re-matches the wallet — by uuid, then rdns, then name, then by querying eth_accounts — and silently reconnects (no prompt) if the wallet still authorizes the account.

License

MIT