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

@numi2/proof-of-funds-snap

v0.1.2

Published

zkpf MetaMask Snap for generating zero-knowledge proof-of-funds attestations

Downloads

11

Readme

zkpf Proof of Funds Snap

A MetaMask Snap for generating zero-knowledge proof-of-funds attestations. Prove you meet a financial threshold without revealing your exact balance.

Overview

This snap enables users to:

  1. Choose a Policy - Select what threshold you want to prove (e.g., ≥10,000 USD)
  2. Select Funding Sources - Connect your Ethereum wallet and/or paste your Zcash UFVK
  3. Bind Your Identity - Sign a message to create a verifiable holder tag

The result is a proof bundle that verifiers can check to confirm you meet the threshold, without learning your actual balance or account details.

Core Flow

┌─────────────────────────────────────────────────────────────────┐
│                   zkpf Proof of Funds Snap                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. CHOOSE POLICY                                               │
│     ┌──────────────────────────────────────────────────────────┐│
│     │ What do you want to prove?                               ││
│     │ ○ Fiat ≥ 10,000 USD                                      ││
│     │ ○ On-chain ≥ 100,000 USD                                 ││
│     │ ● Orchard ≥ 10 ZEC shielded                              ││
│     └──────────────────────────────────────────────────────────┘│
│                                                                  │
│  2. SELECT FUNDING SOURCES                                      │
│     ┌──────────────────────────────────────────────────────────┐│
│     │ Ethereum: 0xabc...def (auto-populated)                   ││
│     │ Zcash:    uview1... (paste UFVK)                         ││
│     └──────────────────────────────────────────────────────────┘│
│                                                                  │
│  3. BIND HOLDER IDENTITY                                        │
│     ┌──────────────────────────────────────────────────────────┐│
│     │ Sign message → holder_tag                                ││
│     │ (keccak256 of signature)                                 ││
│     │                                                          ││
│     │ Verifiers see same identity,                             ││
│     │ never your actual address                                ││
│     └──────────────────────────────────────────────────────────┘│
│                                                                  │
│                    [Create Proof of Funds]                       │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Installation

For Development

  1. Install MetaMask Flask (development version of MetaMask with Snaps support)

  2. Clone and install dependencies:

    cd zkpf-snap
    yarn install
  3. Build for local development:

    ./build_local.sh
  4. Serve the snap:

    yarn serve
  5. The snap will be available at http://localhost:8081

For Production

yarn build

Usage from a Dapp

Step 0: Install the Snap (Required First)

Before calling any snap methods, dapps must request permission using wallet_requestSnaps:

// Install and connect to the snap
const result = await window.ethereum.request({
  method: 'wallet_requestSnaps',
  params: {
    'npm:@numi2/proof-of-funds-snap': {},
  },
});

console.log('Snap installed:', result);
// Returns: { 'npm:@numi2/proof-of-funds-snap': { id, version, enabled, blocked } }

With a specific version:

await window.ethereum.request({
  method: 'wallet_requestSnaps',
  params: {
    'npm:@numi2/proof-of-funds-snap': {
      version: '^0.1.0',
    },
  },
});

Check Installed Snaps

// Get all permitted snaps
const snaps = await window.ethereum.request({
  method: 'wallet_getSnaps',
});

console.log('Installed snaps:', snaps);

Step 1: Choose Policy

// Select a policy to prove against
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'selectPolicy',
      params: {
        policy: {
          policy_id: 100200,
          threshold_raw: 1000000000,
          required_currency_code: 999001,
          verifier_scope_id: 300,
          label: 'Orchard ≥ 10 ZEC shielded',
        },
      },
    },
  },
});

Step 2: Select Funding Sources

// Auto-populate with connected Ethereum address
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'addEthereumSource',
    },
  },
});

// Collect Zcash UFVK via dialogs
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'addZcashSource',
    },
  },
});

// Or add a custom funding source directly
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'addFundingSource',
      params: {
        source: {
          type: 'zcash',
          ufvk: 'uview1...',
          network: 'main',
          snapshotHeight: 2700000,
          balanceZats: 1500000000,
        },
      },
    },
  },
});

Step 3: Bind Holder Identity

// Sign message and generate holder_tag (using personal_sign)
const binding = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'bindHolder',
      params: {
        policy: selectedPolicy,
      },
    },
  },
});

console.log('Holder tag:', binding.holderTag); // "0x..."
console.log('Signature:', binding.signature);
console.log('Signer:', binding.signerAddress);

// Alternative: Use EIP-712 typed data signing
const bindingTyped = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'bindHolderTypedData',
      params: {
        policy: selectedPolicy,
      },
    },
  },
});

Complete Flow

// Create complete proof (combines all steps)
const proofRequest = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'createProof',
      params: {
        policy: selectedPolicy,
      },
    },
  },
});

// proofRequest contains:
// - policy: the selected policy
// - fundingSources: array of funding sources
// - holderBinding: { signature, holderTag, signerAddress, message }
// - timestamp: when the proof was created

Export & Share Proofs

// Export proof as shareable bundle
const bundle = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'exportProofBundle',
      params: {
        proofRequest: proofRequest, // from createProof
      },
    },
  },
});

console.log('Bundle ID:', bundle.bundleId);
console.log('Bundle JSON:', JSON.stringify(bundle));

Verify Proof Bundles

// Verify a proof bundle
const result = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'verifyProofBundle',
      params: {
        bundleJson: '{"version":"1.0.0",...}',
      },
    },
  },
});

console.log('Valid:', result.valid);
console.log('Checks:', result.checks);
console.log('Details:', result.details);

// Interactive verification (prompts user for bundle)
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'verifyBundleInteractive',
    },
  },
});

Proof History

// List all proof history
const history = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'listProofHistory',
    },
  },
});

// Get specific proof from history
const proof = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'getProofFromHistory',
      params: {
        bundleId: 'zkpf-abc123...',
      },
    },
  },
});

// Show history dialog to user
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'showProofHistory',
    },
  },
});

// Clear proof history
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'clearProofHistory',
    },
  },
});

Holder Fingerprint

// Get or create a persistent holder fingerprint
const fingerprint = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'getHolderFingerprint',
    },
  },
});

// Show fingerprint in a dialog
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'showHolderFingerprint',
    },
  },
});

Network Configuration

// Get current network
const network = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'getCurrentNetwork',
    },
  },
});

// Switch network (mainnet/testnet)
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'switchNetwork',
      params: {
        network: 'testnet', // or 'mainnet'
      },
    },
  },
});

// Check if on mainnet
const isMain = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'isMainnet',
    },
  },
});

State Management

// Get current proof state
const state = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'getProofState',
    },
  },
});

// Get raw snap state
const rawState = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'getSnapState',
    },
  },
});

// Get funding sources
const sources = await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'getFundingSources',
    },
  },
});

// Clear funding sources
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'clearFundingSources',
    },
  },
});

// Reset all state
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'resetProofState',
    },
  },
});

// Clear everything including history
await window.ethereum.request({
  method: 'wallet_invokeSnap',
  params: {
    snapId: 'npm:@numi2/proof-of-funds-snap',
    request: {
      method: 'clearSnapState',
    },
  },
});

RPC Methods Reference

Policy & Sources

| Method | Description | Parameters | |--------|-------------|------------| | selectPolicy | Select a policy to prove against | { policy: PolicyDefinition } | | addEthereumSource | Auto-add connected ETH address | None | | addZcashSource | Collect Zcash UFVK via dialogs | None | | addFundingSource | Add a custom funding source | { source: FundingSource } | | getFundingSources | Get current funding sources | None | | clearFundingSources | Clear all funding sources | None |

Holder Identity

| Method | Description | Parameters | |--------|-------------|------------| | bindHolder | Sign with personal_sign | { policy, fundingSources? } | | bindHolderTypedData | Sign with EIP-712 | { policy, fundingSources? } | | getHolderFingerprint | Get/create persistent fingerprint | None | | showHolderFingerprint | Display fingerprint in dialog | None | | getExistingHolderFingerprint | Get fingerprint without creating | None |

Proof Generation & Export

| Method | Description | Parameters | |--------|-------------|------------| | createProof | Complete proof generation | { policy } | | exportProofBundle | Export as shareable bundle | { proofRequest } |

Verification

| Method | Description | Parameters | |--------|-------------|------------| | verifyProofBundle | Verify a proof bundle | { bundleJson: string } | | verifyBundleInteractive | Prompt user and verify | None | | parseProofBundle | Parse bundle without verification | { bundleJson: string } |

Proof History

| Method | Description | Parameters | |--------|-------------|------------| | listProofHistory | Get all proof history | None | | getProofFromHistory | Get specific proof by ID | { bundleId: string } | | showProofHistory | Display history dialog | None | | clearProofHistory | Clear history (with confirmation) | None | | markProofVerified | Mark proof as verified | { bundleId, verified? } |

Network Configuration

| Method | Description | Parameters | |--------|-------------|------------| | getCurrentNetwork | Get current network config | None | | switchNetwork | Switch to mainnet/testnet | { network: 'mainnet' \| 'testnet' } | | getZcashNetwork | Get current Zcash network | None | | getEthereumChainId | Get current chain ID | None | | isMainnet | Check if on mainnet | None |

State Management

| Method | Description | Parameters | |--------|-------------|------------| | getProofState | Get current proof state | None | | resetProofState | Clear proof state | None | | getSnapState | Get raw snap state | None | | setSnapState | Update snap state | Partial state object | | clearSnapState | Clear all state | None |

Holder Tag

The holder_tag = keccak256(signature) enables verifiers to:

  • ✅ Confirm "this bundle was bound to the same MetaMask identity"
  • ✅ Link multiple proofs from the same holder
  • ❌ Learn the actual wallet address
  • ❌ Track the holder across different policies/verifiers

This provides a privacy-preserving way to establish identity consistency.

Permissions

This snap requests the following MetaMask permissions:

| Permission | Purpose | |------------|---------| | snap_dialog | Show confirmation/input dialogs | | snap_manageState | Persist proof state across sessions | | endowment:ethereum-provider | Access Ethereum accounts and signing | | endowment:lifecycle-hooks | Handle installation events | | endowment:rpc | Allow dapps to communicate with the snap |

Security

  • No spending authority: The snap only requests view/sign permissions
  • Local processing: All cryptographic operations happen locally
  • User consent: Every action requires explicit user approval via dialogs
  • No data exfiltration: The snap cannot send data without user action
  • Origin restrictions: Only allowed origins can communicate with the snap

Development

# Install dependencies
yarn install

# Build for local development (adds localhost to allowed origins)
./build_local.sh

# Standard production build
yarn build

# Start development server (watch mode)
yarn start

# Serve built snap
yarn serve

# Run tests
yarn test

# Lint code
yarn lint

For local development, the build_local.sh script automatically adds http://localhost:3000 and http://localhost:5173 to the allowed origins in snap.manifest.json.

License

MIT-0 OR Apache-2.0