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

@noir-wallet/sdk

v0.1.9

Published

TypeScript SDK for Noir Wallet dApp integration

Readme

Noir Wallet SDK

For regular use, install Noir Wallet from the Chrome Web Store. Preview builds for testing are available on the Releases page.

TypeScript SDK and example dApp for integrating with Noir Wallet.

Install

pnpm install

Build

pnpm build
pnpm --filter @noir-wallet/example build

Example

The example app lives in example/ and uses the workspace SDK package:

pnpm example:dev

Noir Wallet must be installed in the browser for wallet connection flows.

Extension compatibility: The optional fundingSource parameter for getMaxTransfer() and sendTransaction() requires Noir Wallet extension 1.0.27 or later. When omitted, the SDK continues to use shielded funds.

SDK API

TypeScript SDK for integrating with Noir Wallet Chrome Extension.

Installation

npm install @noir-wallet/sdk
# or
yarn add @noir-wallet/sdk
# or
pnpm add @noir-wallet/sdk

Usage

Basic Example

import { getNoirWallet } from '@noir-wallet/sdk'

// Get Noir Wallet
const noirWallet = getNoirWallet()
if (!noirWallet) {
  throw new Error('Noir Wallet not installed')
}

const zcash = noirWallet.zcash

// Check existing connection (silent, no popup)
const accounts = await zcash.getAccounts()

// Connect wallet if not connected (shows popup)
if (!accounts) {
  const newAccounts = await zcash.connect()
  console.log('Wallet connected:', newAccounts)
} else {
  console.log('Already connected:', accounts)
}

// Get balance
const balance = await zcash.getBalance()
console.log('Transparent:', balance.transparent, 'ZEC')
console.log('Shielded:', balance.shielded, 'ZEC')
console.log('Available:', balance.available, 'ZEC') // Destination-agnostic fallback

// Get public key
const publicKeyInfo = await zcash.getPublicKey()
if (publicKeyInfo) {
  console.log('Public Key:', publicKeyInfo.pubkey)
  console.log('Address:', publicKeyInfo.address)
}

// Use the same funding source for Max and Send
const fundingSource = 'transparent' as const

// Calculate the exact Max after the destination, memo, and source are known
const max = await zcash.getMaxTransfer({
  to: 'u1XYZ...',
  memo: 'Payment for services',
  fundingSource
})

// Send transaction with optional memo
const txid = await zcash.sendTransaction({
  to: 'u1XYZ...',
  amount: max.maxAmount,
  memo: 'Payment for services',
  fundingSource
})
console.log('Transaction sent:', txid)

// Sign message
const result = await zcash.signMessage('Hello World')
console.log('Signature:', result.signature)
console.log('Address:', result.address)

Detect Provider

import { getNoirWallet, isNoirWalletInstalled } from '@noir-wallet/sdk'

// Check if installed
if (isNoirWalletInstalled()) {
  const noirWallet = getNoirWallet()
  console.log('Noir Wallet detected')
} else {
  console.error('Noir Wallet not found')
}

Event Listeners

const noirWallet = getNoirWallet()
const zcash = noirWallet.zcash

// Connect first
await zcash.connect()

// Listen to account changes (unlock/lock, switch account)
zcash.on('accountsChanged', async addresses => {
  if (!addresses) {
    console.log('Wallet locked or disconnected')
    return
  }
  console.log('Primary account changed:', addresses.transparent)
  // Multi-wallet dApps: refresh the authorized account list
  const result = await zcash.getAccounts()
  console.log('Authorized wallets:', result?.accounts.length)
})

// Listen to chain/network changes
zcash.on('chainChanged', chainInfo => {
  console.log('Network changed:', chainInfo)
})

API

Methods

All methods are available on noirWallet.zcash:

connect()

Request wallet connection (shows popup if not authorized). The approval screen lets the user authorize one or several independent wallets in a single action (MetaMask-style: the current account is preselected, more can be added).

Returns: Promise<ZcashConnectResult> — the primary account's transparent/shielded addresses plus an accounts array listing every authorized wallet.

const result = await zcash.connect()

// Primary (connected) account — same shape as before, fully backward compatible
console.log('Transparent:', result.transparent)
console.log('Shielded:', result.shielded)

// Every authorized wallet (always contains at least the primary)
result.accounts.forEach(acc => {
  console.log(acc.label, acc.addresses.transparent, acc.addresses.shielded)
})

getAccounts()

Query existing connection silently (no popup).

Returns: Promise<ZcashConnectResult | null> — same enhanced shape as connect(), or null if not connected.

const result = await zcash.getAccounts()
if (result) {
  console.log('Connected:', result.transparent)
  console.log('Authorized wallets:', result.accounts.length)
} else {
  console.log('Not connected')
}

getBalance(accountId?)

Get wallet balance.

Params (optional):

  • accountId: string — an account id (the ${walletId}:${accountId} key from accounts). Omit to read the primary (connected) account.

Returns: Promise<ZcashBalanceResult> — the primary (or requested) account's balance fields plus an accounts array with every authorized account's balance.

// Primary account balance (backward compatible)
const balance = await zcash.getBalance()
console.log('Shielded:', balance.shielded, 'ZEC')
console.log('Available:', balance.available, 'ZEC') // Destination-agnostic fallback

// Per-wallet balances
balance.accounts.forEach(b => {
  console.log(b.id, b.balance.shielded, b.synced ? '(synced)' : '(cached)')
})

// A specific authorized wallet
const second = await zcash.getBalance(balance.accounts[1]?.id)

Balance fields:

  • transparent: Transparent address balance
  • shielded: Shielded balance (Sapling + Orchard)
  • total: Total balance (transparent + shielded)
  • available: A destination-agnostic, cached compatibility value. It does not account for the final recipient, memo, selected fee tier, or exact transaction action count. Use getMaxTransfer() for an exact Max value once those inputs are known.
  • accounts: Balance of every authorized account; each entry carries a synced flag (false = cached/zero fallback because the wallet is locked or that account hasn't synced yet).

Multi-wallet access & compatibility: connect() / getAccounts() / getBalance() are backward compatible — their original top-level fields are unchanged, and the accounts array is purely additive. dApps on older extensions that don't return accounts still work: the SDK normalizes the single-account response into a one-element accounts array, so your code path is identical regardless of extension version. To react to changes, listen for accountsChanged and re-call getAccounts() / getBalance() to refresh the array.

getMaxTransfer(params)

Calculate the exact transferable amount and proposal fee for a specific recipient, memo, and fee tier using the current connected account.

Params:

  • to: string - Recipient address
  • memo?: string - Private memo (max 512 bytes UTF-8, shielded recipients only)
  • feeTier?: 'standard' | 'fast' - Fee tier used for the estimate; defaults to standard
  • fundingSource?: 'shielded' | 'transparent' - Balance used for the estimate; defaults to shielded

Returns: Promise<MaxTransferEstimate>

  • maxAmount: string - Exact payment amount in ZEC
  • fee: string - Fee for the exact send-max proposal in ZEC
const params = {
  to: 'u1XYZ...',
  memo: 'Payment for services',
  feeTier: 'standard' as const,
  fundingSource: 'transparent' as const
}
const { maxAmount, fee } = await zcash.getMaxTransfer(params)

console.log('Max:', maxAmount, 'ZEC')
console.log('Fee:', fee, 'ZEC')

const txid = await zcash.sendTransaction({
  to: params.to,
  amount: maxAmount,
  memo: params.memo,
  fundingSource: params.fundingSource
})

Compatibility: fundingSource requires Noir Wallet extension 1.0.27 or later. Omit it to retain the existing shielded-only behavior. Do not request transparent funding from an older extension because it does not understand the parameter. Legacy dApps can continue using balance.available, but it is a conservative fallback rather than an exact recipient-aware Max.

getPublicKey(options?)

Get the public key of the transparent address.

Params (optional):

  • options.signingMode: 'current' (default) or 'derived'

Returns: Promise<{ pubkey: string, address: string, signingMode: SigningMode, originAddress?: string } | null>

  • pubkey: Hex-encoded public key
  • address: Transparent address corresponding to the key
  • signingMode: The actual signing mode used
  • originAddress: (only in 'derived' mode) The user's main transparent address
// Default: current transparent address key
const publicKeyInfo = await zcash.getPublicKey()

// Derived: privacy-preserving key (unlinkable to main address)
const derivedKey = await zcash.getPublicKey({ signingMode: 'derived' })
console.log('Derived Key:', derivedKey.pubkey)
console.log('Main Address:', derivedKey.originAddress)

Note: This method requires the wallet to be connected but does not trigger an unlock popup. Returns null if the wallet is locked.

sendTransaction(params)

Send a transaction using shielded funds by default, or explicitly select transparent funds.

Params:

  • to: string - Recipient address
  • amount: string - Amount in ZEC
  • memo?: string - Private memo (max 512 bytes UTF-8, shielded recipients only; not allowed for transparent recipients)
  • fundingSource?: 'shielded' | 'transparent' - Balance used to fund the transaction; defaults to shielded

Returns: Promise<string> - Transaction ID

Privacy: Transparent funding reveals and may link the selected transparent UTXOs on-chain. It is not supported by Keystone hardware wallets.

Compatibility: fundingSource is supported by Noir Wallet extension 1.0.27 or later.

const txid = await zcash.sendTransaction({
  to: 'u1XYZ...',
  amount: '0.1',
  memo: 'Payment for services',
  fundingSource: 'transparent'
})

signMessage(message, options?)

Sign a message with a transparent address key.

Params:

  • message: string - Message to sign
  • options.signingMode: 'current' (default) or 'derived'

Returns: Promise<SignMessageResult>

  • signature: Hex-encoded ECDSA signature
  • pubkey: Hex-encoded public key
  • address: Transparent address used for signing
  • signingMode: The actual signing mode used
  • originAddress: (only in 'derived' mode) The user's main transparent address
// Default: sign with current transparent address key
const result = await zcash.signMessage('Hello World')

// Derived: sign with a privacy-preserving derived key
// Recommended for identity binding (MCA, DID) to prevent on-chain asset linkage
const derived = await zcash.signMessage('Hello World', { signingMode: 'derived' })
console.log('Signature:', derived.signature)
console.log('Origin Address:', derived.originAddress)

getAddresses()

Get the connected wallet's transparent and shielded addresses.

Returns: Promise<ZcashAddress> - { transparent, shielded }

const addresses = await zcash.getAddresses()
console.log('Transparent:', addresses.transparent)
console.log('Shielded:', addresses.shielded)

shieldFunds()

Shield transparent funds to the private (shielded) balance. Requires user approval via popup.

Returns: Promise<string> - Transaction ID

const txid = await zcash.shieldFunds()
console.log('Shield transaction:', txid)

Note: This moves all transparent balance into the shielded pool for enhanced privacy. The user will see an approval popup.

getTransactionHistory()

Fetch transaction history from the wallet (includes on-chain and local pending transactions).

Returns: Promise<TransactionHistoryEntry[]>

Each entry contains:

  • txid: Transaction hash (hex)
  • type: 'send' | 'receive' | 'shield' | 'swap' | 'lending_supply' | 'lending_withdraw' | 'lending_claim'
  • amount: Amount in ZEC
  • status: 'mined' | 'pending' | 'failed'
  • timestamp: Unix timestamp in milliseconds
  • memo: Optional memo string
const history = await zcash.getTransactionHistory()
history.forEach(tx => {
  console.log(`${tx.type} ${tx.amount} ZEC - ${tx.status}`)
})

switchNetwork(network) (deprecated)

Deprecated: Mainnet and testnet are now separate extension builds. Install the testnet extension for testnet usage. This method is retained for backward compatibility but has no effect.

Utility Functions

publicKeyToAddress(pubkey, network)

Convert a public key to a Zcash transparent address.

Params:

  • pubkey: string - Public key in hexadecimal format (compressed 33 bytes or uncompressed 65 bytes)
  • network: 'mainnet' | 'testnet' - Network type (defaults to 'mainnet')

Returns: string - Zcash transparent address (P2PKH format)

Throws: Error if public key format is invalid

import { publicKeyToAddress } from '@noir-wallet/sdk'

// Get public key from wallet
const { pubkey } = await zcash.getPublicKey()

// Convert to address for verification
const address = publicKeyToAddress(pubkey, 'mainnet')
console.log('Address:', address)

// Convert external public key
const externalPubkey = '03a1b2c3d4e5f6...'
const externalAddress = publicKeyToAddress(externalPubkey, 'mainnet')

Public Key Formats:

  • Compressed (33 bytes): Starts with 02 or 03
  • Uncompressed (65 bytes): Starts with 04

Note: This function implements the Bitcoin/Zcash P2PKH address generation algorithm (SHA256 → RIPEMD160 → Base58Check).

Events

accountsChanged

Triggered when accounts change (unlock/lock, switch account).

Data: ZcashAddress | null - Current addresses (null if locked/disconnected)

chainChanged

Triggered when network changes.

Data: { chainId: string, network: string }

Multi-wallet dApps: there is no separate batch event. When accountsChanged fires, re-call getAccounts() (and getBalance()) to refresh the accounts array.

Types

interface ZcashAddress {
  transparent: string
  shielded: string
}

interface Balance {
  transparent: string
  shielded: string
  total?: string
  available?: string // Destination-agnostic compatibility value
}

// One account from a batch (multi-wallet) authorization.
// `id` is the stable `${walletId}:${accountId}` key; `label` is the wallet name.
interface ZcashAccount {
  id: string
  label: string
  walletId: string
  accountId: string
  addresses: ZcashAddress
}

// Balance for one authorized account.
// `synced: false` means a cached/zero fallback (locked or not yet synced).
interface ZcashAccountBalance {
  id: string
  walletId: string
  accountId: string
  balance: Balance
  synced: boolean
}

// Result of connect() / getAccounts(): primary account fields + every authorized wallet.
interface ZcashConnectResult extends ZcashAddress {
  accounts: ZcashAccount[]
}

// Result of getBalance(): primary (or requested) balance + every authorized balance.
interface ZcashBalanceResult extends Balance {
  accounts: ZcashAccountBalance[]
}

interface SendTransactionParams {
  to: string
  amount: string
  memo?: string // Private memo (max 512 bytes UTF-8)
}

type FeeTier = 'standard' | 'fast'

interface MaxTransferParams {
  to: string
  memo?: string
  feeTier?: FeeTier
}

interface MaxTransferEstimate {
  maxAmount: string
  fee: string
}

interface TransactionHistoryEntry {
  txid: string
  type: string      // 'send' | 'receive' | 'shield' | 'swap' | 'lending_supply' | 'lending_withdraw' | 'lending_claim'
  amount: string    // ZEC amount
  status: string    // 'mined' | 'pending' | 'failed'
  timestamp: number // Unix ms
  memo?: string
}

type SigningMode = 'derived' | 'current'

interface SignMessageOptions {
  signingMode?: SigningMode // Default: 'current'
}

interface SignMessageResult {
  signature: string // Hex-encoded ECDSA signature
  pubkey: string // Hex-encoded public key
  address: string // Transparent address used for signing
  signingMode: SigningMode // Actual signing mode used
  originAddress?: string // Main transparent address (only in 'derived' mode)
}

type Network = 'mainnet' | 'testnet'

Error Handling

const noirWallet = getNoirWallet()
if (!noirWallet) {
  console.error('Please install Noir Wallet extension')
  return
}

try {
  await noirWallet.zcash.connect()
} catch (error) {
  if (error.code === 4001) {
    console.error('User rejected the request')
  } else {
    console.error('Connection failed:', error.message)
  }
}

License

MIT