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

@asentum/metamask-snap

v0.1.0

Published

MetaMask Snap for AsentumChain. Manages Dilithium3 keys and signs AsentumChain transactions inside MetaMask.

Downloads

159

Readme

@asentum/metamask-snap

Post-quantum signing for AsentumChain, inside MetaMask.

This is a MetaMask Snap that gives MetaMask users a native AsentumChain account without forking MetaMask and without compromising the chain's post-quantum security property. The Dilithium3 private key lives only inside the Snap sandbox; the user's existing secp256k1 Ethereum accounts are untouched and sit alongside the AsentumChain account in the same MetaMask UI.

What it does

  • Derives a Dilithium3 keypair from the user's MetaMask seed phrase (via snap_getEntropy). Backing up / restoring the MetaMask recovery phrase also backs up / restores the AsentumChain account: no extra seed to remember.
  • Builds AsentumChain transactions (transfer, contract call, contract deploy) in the chain's native SSZ + Dilithium3 format.
  • Signs those transactions with the derived keypair after showing the user a confirmation dialog that spells out to-address, value, method name, args, gas: everything they're authorizing.
  • Returns the signed bytes to the calling dApp, which broadcasts them to an AsentumChain RPC endpoint. The Snap itself never touches the network.

What it does NOT do

  • It does not speak Ethereum. The transactions it signs are pure AsentumChain: Dilithium3 signatures, BLAKE3 hashes, SSZ serialization. An attempt to submit a Snap-signed transaction to an Ethereum node would be meaningless.
  • It does not hold ETH or any Ethereum-ecosystem balance. The user's AsentumChain address is derived from Dilithium3, which is completely separate from their secp256k1 Ethereum address. They are distinct accounts with distinct balances.
  • It does not broadcast transactions. It signs and hands the bytes back. This keeps the Snap's permission surface small (snap_getEntropy + snap_dialog only: no endowment:network-access) and leaves the RPC choice to the dApp.

Snap RPC surface

Invoke via the standard MetaMask Snap interface:

const result = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@asentum/metamask-snap',
    request: { method: 'asentum_getAddress' },
  },
});

Read-only methods (no confirmation dialog)

| Method | Returns | | ----------------------- | ------------------------------------------------------------- | | asentum_getAddress | string: 0x-prefixed 20-byte address | | asentum_getPublicKey | string: 0x-prefixed Dilithium3 public key (1952 bytes) | | asentum_buildTransfer | { body }: an unsigned tx body preview (for UI rendering) |

Signing methods (each shows a confirmation dialog)

| Method | Params | Returns | | ---------------------- | ------------------------------------------------------------------------------------------ | ------------------- | | asentum_signTransfer | { chainId, nonce, to, value, gasLimit?, maxFeePerGas?, maxPriorityFeePerGas? } | { rawTx: string } | | asentum_signCall | { chainId, nonce, to, value, method, args, gasLimit?, maxFeePerGas?, maxPriorityFeePerGas? } | { rawTx: string } | | asentum_signDeploy | { chainId, nonce, source, value?, gasLimit?, maxFeePerGas?, maxPriorityFeePerGas? } | { rawTx: string } |

All numeric params are strings (decimal or 0x-hex) because JSON-RPC can't represent bigints natively. to is a 0x-prefixed 20-byte hex address. rawTx is 0x-prefixed SSZ-encoded SignedTransaction bytes ready for POST /tx on any AsentumChain node.

Example dApp snippet

// 1. Request the Snap be installed (first time only).
await window.ethereum.request({
  method: 'wallet_requestSnaps',
  params: { 'npm:@asentum/metamask-snap': {} },
});

// 2. Get the user's AsentumChain address.
const address = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@asentum/metamask-snap',
    request: { method: 'asentum_getAddress' },
  },
});
console.log('AsentumChain address:', address);

// 3. Look up the current nonce via the Ethereum JSON-RPC compat layer
//    (Phase 4.1) that any AsentumChain node exposes at POST /.
const res = await fetch('http://127.0.0.1:8545/', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'eth_getTransactionCount',
    params: [address, 'latest'],
  }),
});
const { result: nonceHex } = await res.json();

// 4. Build + sign a transfer via the Snap. MetaMask will show a
//    confirmation dialog with the tx details before signing.
const { rawTx } = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@asentum/metamask-snap',
    request: {
      method: 'asentum_signTransfer',
      params: {
        chainId: '1337',
        nonce: nonceHex, // hex string from eth_getTransactionCount
        to: '0x' + '42'.repeat(20),
        value: '1000000000000000000', // 1 ASE in wei
      },
    },
  },
});

// 5. Broadcast the signed tx to the AsentumChain node.
await fetch('http://127.0.0.1:8545/tx', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ rawTx }),
});

That's the full flow: install Snap → get address → lookup nonce via eth-compat → sign via Snap → broadcast via native RPC.

Development

The Snap source lives in src/. The testable crypto and tx-building logic is in src/wallet-core.ts; the MetaMask-specific glue (confirmation dialogs, entropy loading, the onRpcRequest dispatcher) is in src/index.ts. This split means almost all of the important behavior can be unit-tested without running MetaMask: test/wallet-core.test.ts covers the primitives, and test/snap-handler.test.ts mocks the snap global to exercise the dispatcher end-to-end.

# Run the full test suite
pnpm --filter @asentum/metamask-snap test

# Typecheck
pnpm --filter @asentum/metamask-snap typecheck

# Build (produces dist/index.js for Snap packaging)
pnpm --filter @asentum/metamask-snap build

Publishing to a real MetaMask instance

For local testing against a real MetaMask Flask installation, you'll need @metamask/snaps-cli:

# Install once (not in the workspace: use your global environment)
npm install -g @metamask/snaps-cli

# Bundle the Snap into a single file MetaMask can load
mm-snap build

# Serve locally for MetaMask Flask to install from
mm-snap serve

Then in MetaMask Flask, use wallet_requestSnaps with snapId: 'local:http://localhost:8080' to install from the local build.

When publishing to NPM for production use, swap the local type declaration (./snap-api.js) for the real MetaMask SDK:

// Before (development):
import type { OnRpcRequestHandler, OnRpcRequestArgs } from './snap-api.js';

// After (publishing):
import type { OnRpcRequestHandler, OnRpcRequestArgs } from '@metamask/snaps-sdk';

And install @metamask/snaps-sdk as a dependency before publishing.

Security notes

  • The Dilithium3 private key never leaves the Snap sandbox. It's derived on-the-fly from snap_getEntropy every time a signing method runs, used once, and the in-memory copy is discarded.
  • Every signing method shows a confirmation dialog. The user sees the full tx shape (to, value, method, args, chain, nonce, gas) before approving. Signing cannot happen silently.
  • The origin of the calling dApp is included in the confirmation dialog. Users can verify they're signing for the site they expect.
  • The Snap has no network access permission. It can't phone home, can't exfiltrate the key, can't observe network traffic. All it can do is sign what the dApp gives it.