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

@pulse-protocol/crypto

v0.1.0

Published

TypeScript cryptographic primitives for the Pulse Protocol. Mirrors [`pulse-protocol-go/crypto`](https://github.com/smarter-contracts/pulse-protocol-go) with byte-identical output.

Readme

@pulse-protocol/crypto

TypeScript cryptographic primitives for the Pulse Protocol. Mirrors pulse-protocol-go/crypto with byte-identical output.

Installation

pnpm add @pulse-protocol/crypto @pulse-protocol/types

API

Hashing

import { pulseHashBytes, pulseHashString, toHex, fromHex } from '@pulse-protocol/crypto'

const hash = pulseHashBytes(new TextEncoder().encode('hello'))  // Keccak-256, Uint8Array
const hex  = toHex(hash)
const back = fromHex(hex)

pulseHashString('hello')  // convenience — UTF-8 encodes then hashes

HD Wallet derivation

Derives secp256k1 and ML-KEM-768 key pairs from a BIP32 master key using Pulse-specific derivation paths.

import { masterKeyFromSeed, derivePublicKey, derivePrivateKey, derivePqKeyPair, pqKeyFingerprint } from '@pulse-protocol/crypto'
import { PulsePurpose } from '@pulse-protocol/types'

const masterKey = masterKeyFromSeed(seed)  // HDKey from 16–64 byte seed

// secp256k1 keys
const pubKey  = derivePublicKey(masterKey, otherPartyId, chainId, consentNumber, PulsePurpose.SignTx)
const privKey = derivePrivateKey(masterKey, otherPartyId, chainId, consentNumber, PulsePurpose.SignTx)

// ML-KEM-768 post-quantum key pair (64-byte seed → 1184-byte pubKey, 2400-byte secretKey)
const { publicKey, secretKey } = derivePqKeyPair(
  masterKey, otherPartyId, consentNumber, chainId, PulsePurpose.PQDeriveConsent
)

// Keccak-256 fingerprint of an ML-KEM-768 public key (32 bytes)
const fp = pqKeyFingerprint(publicKey)

Derivation path format: m/44'/60'/<purpose>'/<chainId>'/<otherParty>'/<consentNumber>'


ECDH encryption (two-party, secp256k1)

import { encryptEcdh, decryptEc } from '@pulse-protocol/crypto'
import { PulsePurpose } from '@pulse-protocol/types'

const encrypted = encryptEcdh(
  plaintext,
  contractAddress,   // hex Ethereum address, e.g. '0xabc...'
  myPrivateKey,      // 32-byte secp256k1 private key
  otherPublicKey,    // 33-byte compressed secp256k1 public key
  PulsePurpose.EncryptConsentStructure,
  chainId,
  consentNumber,
)
// encrypted: PulseECEncryptionResult { sealedData, key1, key2 }

const plaintext = decryptEc(encrypted, contractAddress, myPrivateKey,
  PulsePurpose.EncryptConsentStructure, chainId, consentNumber)

Post-quantum encryption (multi-party, ML-KEM-768)

Encrypts for one or more recipients. Each recipient can decrypt independently with their private key.

import { encryptPq, decryptPq } from '@pulse-protocol/crypto'
import { PulsePurpose } from '@pulse-protocol/types'

const encrypted = encryptPq(
  plaintext,
  contractAddress,
  [alicePublicKey, bobPublicKey],   // ML-KEM-768 public keys (1184 bytes each)
  PulsePurpose.EncryptConsentStructure,
  chainId,
  consentNumber,
)
// encrypted: PulsePQEncryptionResult { sealedData, keys: [ {keyFingerPrint, ...}, ... ] }

const plaintext = decryptPq(
  encrypted,
  contractAddress,
  bobSecretKey,    // ML-KEM-768 private key (2400 bytes)
  bobPublicKey,    // ML-KEM-768 public key (1184 bytes)
  PulsePurpose.EncryptConsentStructure,
  chainId,
  consentNumber,
)

EIP-191 signing

Signs and recovers Ethereum addresses using EIP-191 ("\x19Ethereum Signed Message:\n32").

import { signConsent, signRevoke, getConsentAddress, getRevokeAddress } from '@pulse-protocol/crypto'

// Sign
const sig = signConsent(privateKeyBytes, contractAddress, cid)
// sig: Uint8Array (65 bytes) — r(32) || s(32) || v(1), v = 27 or 28

const sig = signRevoke(privateKeyBytes, contractAddress, cid, rcid)

// Recover address
const addr = getConsentAddress(sig, contractAddress, cid)  // 20-byte Uint8Array
const addr = getRevokeAddress(sig, contractAddress, cid, rcid)

Context hash

Domain-separation hash binding a (chainId, contractAddress, consentNumber) tuple. Used as AAD in all encryption operations.

import { contextString, contextHash } from '@pulse-protocol/crypto'

contextString(1, '0x0102...', 2)
// → '|pulse|ctx|v1|chain=1|contract=0x0102...|consentNumber=2'

contextHash(1, '0x0102...', 2)  // Keccak-256 of the above, Uint8Array (32 bytes)

AES-256-GCM (low-level)

import { pulseSeal, pulseOpen, pulseSealWithNewKey, AES_KEY_SIZE, AES_NONCE_SIZE } from '@pulse-protocol/crypto'

// Generate a new key + nonce and seal
const { ciphertext, aesKey, nonce } = pulseSealWithNewKey(
  plaintext, purpose, cipherSuite, recipientHash, contextHash
)

// Seal with an existing key/nonce
const ciphertext = pulseSeal(plaintext, aesKey, nonce, purpose, cipherSuite,
  recipientHash, contextHash, transcriptHash)

// Open
const plaintext = pulseOpen(ciphertext, aesKey, nonce, purpose, cipherSuite,
  recipientHash, contextHash, transcriptHash)

HKDF-Keccak-256 (low-level)

import { pulseHkdfEcdh, pulseHkdfKyber, pulseHkdfPqSeed } from '@pulse-protocol/crypto'

// ECDH key derivation
const { key, nonce } = pulseHkdfEcdh(sharedSecret, transcriptHash, null, contextHash)

// ML-KEM key derivation
const { key, nonce } = pulseHkdfKyber(sharedSecret, encapsulatedKey, fingerprint, contextHash)

// ML-KEM seed derivation (HD wallet → ML-KEM seed)
const seed64 = pulseHkdfPqSeed(nodePrivKey, nodePubKey, otherPartyStr, contextHash)