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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@l8d/viem-account-yubikey

v0.1.2

Published

Helper library to use YubiKey hardware security keys with viem

Downloads

351

Readme

@l8d/viem-account-yubikey

Use YubiKey hardware security keys for signing Ethereum transactions and messages with viem.

This package enables you to create viem accounts that use YubiKey's OpenPGP card applet for signing operations, providing hardware-based security for your Ethereum interactions.

Features

  • 🔐 Hardware Security: Sign transactions using YubiKey's secure element
  • 🔌 Viem Integration: Works seamlessly with viem's wallet client
  • 🎯 OpenPGP Slots: Support for all three OpenPGP key slots (Signature, Authentication, Decryption)
  • 🔍 Auto-Discovery: Automatically find which slot controls your Ethereum address
  • Type-Safe: Full TypeScript support with type definitions
  • 📦 Zero Config: Works out of the box with sensible defaults

Installation

npm install @l8d/viem-account-yubikey viem@^2.0.0 pcsclite@^1.0.0

System Requirements

  • Node.js: 18 or higher
  • PC/SC Daemon:
    • macOS: Built-in, works out of the box
    • Linux: Install pcscd (sudo apt install pcscd on Debian/Ubuntu)
    • Windows: Built-in smart card service
  • YubiKey: With secp256k1 keys configured in OpenPGP applet

Quick Start

import { yubikeyToAccount } from '@l8d/viem-account-yubikey';
import { createWalletClient, http } from 'viem';
import { mainnet } from 'viem/chains';

// Create YubiKey account
const account = await yubikeyToAccount({
  address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  // Optional: specify PIN (defaults to '123456')
  pin: '123456',
  // Optional: slot will be auto-discovered if not specified
});

// Create wallet client
const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
});

// Send transaction
const hash = await client.sendTransaction({
  to: '0x...',
  value: 1000000000000000000n, // 1 ETH
});

console.log('Transaction hash:', hash);

Usage

Basic Account Creation

import { yubikeyToAccount } from '@l8d/viem-account-yubikey';

// Minimal configuration (slot will be auto-discovered)
const account = await yubikeyToAccount({
  address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
});

Explicit Slot Specification

For better performance, you can explicitly specify which OpenPGP slot to use:

import { yubikeyToAccount, OpenPGPSlot } from '@l8d/viem-account-yubikey';

const account = await yubikeyToAccount({
  address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  slot: OpenPGPSlot.Signature, // or Authentication, or Decryption
  pin: '123456',
});

Available OpenPGP Slots

YubiKey's OpenPGP applet has three key slots:

enum OpenPGPSlot {
  Signature = 0xb6,      // Primary signing slot
  Authentication = 0xa4,  // Authentication operations
  Decryption = 0xb8,      // Encryption/decryption operations
}

All three slots can hold secp256k1 keys and be used for Ethereum signing.

Signing Messages

// Sign a personal message (EIP-191)
const signature = await account.signMessage({
  message: 'Hello, Ethereum!',
});

Signing Typed Data

// Sign EIP-712 typed data
const signature = await account.signTypedData({
  domain: {
    name: 'Ether Mail',
    version: '1',
    chainId: 1,
    verifyingContract: '0x...',
  },
  types: {
    Person: [
      { name: 'name', type: 'string' },
      { name: 'wallet', type: 'address' },
    ],
  },
  primaryType: 'Person',
  message: {
    name: 'Bob',
    wallet: '0x...',
  },
});

Direct Signing (Without Client)

You can also use the account directly without creating a wallet client:

const account = await yubikeyToAccount({
  address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
});

// Sign transaction
const signedTx = await account.signTransaction({
  to: '0x...',
  value: 1000000000000000000n,
  chainId: 1,
  nonce: 42,
  maxFeePerGas: 100n,
  maxPriorityFeePerGas: 100n,
});

// Sign message
const signature = await account.signMessage({
  message: 'Hello!',
});

Configuration Options

interface YubikeyAccountConfig {
  /**
   * The Ethereum address controlled by the YubiKey
   */
  address: `0x${string}`;

  /**
   * Optional: Explicitly specify which OpenPGP slot to use
   * If not provided, the slot will be auto-discovered
   */
  slot?: OpenPGPSlot;

  /**
   * Optional: OpenPGP card PIN (default: "123456")
   * Required for signing operations
   */
  pin?: string;

  /**
   * Optional: Timeout for signing operations in milliseconds (default: 30000)
   */
  timeout?: number;

  /**
   * Optional: Enable debug logging
   */
  debug?: boolean;
}

YubiKey Setup

Before using this package, you need to:

  1. Configure secp256k1 keys in your YubiKey's OpenPGP applet
  2. Know the Ethereum address derived from your YubiKey's public key

Configuring Your YubiKey

You can use the YubiKey Manager (ykman) or GPG to configure OpenPGP keys:

# Install YubiKey Manager
brew install ykman  # macOS
# or
sudo apt install yubikey-manager  # Linux

# View current OpenPGP configuration
ykman openpgp info

For importing existing secp256k1 keys or generating new ones, refer to the YubiKey OpenPGP documentation.

Finding Your Ethereum Address

If you already have a secp256k1 key on your YubiKey but don't know the corresponding Ethereum address, you can use utilities from the parent @l8d/hardhat-yubikey package to discover it.

Transaction Types Support

This package supports all Ethereum transaction types:

  • Legacy Transactions (Type 0)
  • EIP-2930 Access List Transactions (Type 1)
  • EIP-1559 Dynamic Fee Transactions (Type 2)

The v value is automatically calculated based on the transaction type and chain ID.

Error Handling

try {
  const account = await yubikeyToAccount({
    address: '0x...',
  });
} catch (error) {
  if (error.message.includes('PC/SC')) {
    console.error('YubiKey connection failed. Is it plugged in?');
  } else if (error.message.includes('PIN verification failed')) {
    console.error('Incorrect PIN provided');
  } else if (error.message.includes('Could not find OpenPGP slot')) {
    console.error('Address not found on any YubiKey slot');
  } else {
    console.error('Unexpected error:', error);
  }
}

Debug Logging

Enable debug logging to troubleshoot issues:

// Enable for specific account
const account = await yubikeyToAccount({
  address: '0x...',
  debug: true,
});

// Or enable via environment variable
DEBUG=viem-account-yubikey:* node your-script.js

How It Works

  1. Connection: Uses PC/SC (Personal Computer/Smart Card) interface to communicate with YubiKey
  2. OpenPGP Applet: Sends APDU commands to YubiKey's OpenPGP card application
  3. ECDSA Signing: YubiKey performs ECDSA signing using secp256k1 curve
  4. Signature Recovery: Recovers the Ethereum v value by trying both recovery IDs
  5. Format Conversion: Converts OpenPGP signature format to Ethereum signature format

Security Considerations

  • 🔐 Private keys never leave the YubiKey: All signing happens on the secure element
  • 🔑 PIN Protection: YubiKey requires PIN verification before signing
  • 🛡️ Physical Confirmation: Some YubiKey models require physical touch for signing
  • ⚠️ Default PIN: Change the default PIN (123456) after initial setup!

Limitations

  • Slot Auto-Discovery: First-time slot discovery requires trying all three slots, which may take a few seconds
  • Single Device: Currently supports one YubiKey at a time
  • PC/SC Required: Requires PC/SC daemon to be running on the system

Related Packages

Troubleshooting

"Failed to initialize PC/SC"

  • macOS: PC/SC should work out of the box
  • Linux: Install and start pcscd: sudo apt install pcscd && sudo systemctl start pcscd
  • Windows: Ensure Smart Card service is running

"Could not find OpenPGP slot"

  • Verify your YubiKey has secp256k1 keys configured
  • Check that the address matches the public key on your YubiKey
  • Try explicitly specifying the slot

"PIN verification failed"

  • Default PIN is 123456
  • After 3 failed attempts, PIN will be blocked
  • Use PUK to unlock if blocked

Contributing

Contributions are welcome! Please see the main repository for contribution guidelines.

License

MIT

Author

Tenor Biel (@l8d)

Acknowledgments

This package is inspired by:


Need help? Open an issue on GitHub.

viem-account-yubikey