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

mempool-protection

v0.2.0

Published

TypeScript primitives for ordinal marketplace mempool sniping protection.

Readme

mempool-protection

TypeScript library for building mempool-sniping-protected ordinals listings with one function.

Install

npm install mempool-protection

API

The package intentionally exports a single workflow function:

  • createProtectedListingFlow(params)

It builds:

  1. lock transaction PSBT (seller inscription UTXO -> taproot output with 2-of-2 script path)
  2. protected sale PSBT (script-path spend of locked UTXO with required sighash model)

Minimal Example

import * as bitcoin from "bitcoinjs-lib";
import * as ecc from "tiny-secp256k1";
import { createProtectedListingFlow } from "mempool-protection";

bitcoin.initEccLib(ecc);

const network = bitcoin.networks.testnet;

const flow = createProtectedListingFlow({
  network,
  inscriptionInput: {
    txid: "00".repeat(32),
    vout: 0,
    value: 343,
    witnessUtxo: {
      script: Buffer.from("0014...hex...", "hex"),
      value: 343,
    },
  },
  sellerPubKey: Buffer.from("02...hex...", "hex"),
  marketplacePubKey: Buffer.from("03...hex...", "hex"),
  committedOutput: {
    address: "tb1q...",
    value: 330,
  },
  marketFeeRateBps: 0,
});

// flow.lockingPsbt
// flow.salePsbt

Params

Required:

  • network
  • inscriptionInput
  • sellerPubKey
  • marketplacePubKey
  • committedOutput
  • marketFeeRateBps

Optional (only when needed):

  • gasFundingInput: required only if inscriptionInput.value < 343; when present, minimum value is Math.ceil(vbytes(tx1) * minRelayFeeRateSatPerVb) and tx1 inputs are limited to wrapped segwit, native segwit, or taproot
    • for taproot funding inputs, you can include tapInternalKey on the input object (32-byte x-only or 33-byte compressed key)
  • minRelayFeeRateSatPerVb: optional relay floor used for gas-funding minimum calculation; defaults to 0.1
  • marketFeeBasisSats: if fee basis differs from committedOutput.value
  • multisigTaprootInternalPubKey: optional key-spend pubkey for the lock output taproot input (32-byte x-only or 33-byte compressed key)
    • default order when omitted: inscriptionInput.tapInternalKey (if inscription input is taproot), then gasFundingInput.tapInternalKey (if gas input is taproot), then sellerPubKey
  • signers: include only if the function should also sign (and optionally finalize) seller multisig input
    • signers must support signSchnorr for taproot script-path signing

Return value

createProtectedListingFlow returns:

  • lockingPsbt
  • salePsbt
  • multisig
  • plannedLockedUtxo
  • lockedValueSats
  • marketFeeSats
  • prefundingReclaimSats

Notes

  • tx1 should meet relay floor fee rate: fee(tx1) / vbytes(tx1) >= minRelayFeeRateSatPerVb (default 0.1 sat/vB).
  • salePsbt from this function is a listing template: exactly 1 input (the locked UTXO) and 1 output (committedOutput).
  • Sighash behavior for protected sale input is fixed:
    • seller: SIGHASH_SINGLE | SIGHASH_ANYONECANPAY
    • marketplace: SIGHASH_ALL
  • Seller unilateral cancellation remains intact in both stages:
    • tx1 perspective: cancellation is trivial because seller controls the funding input(s) and can simply replace/cancel before confirmation.
    • tx2 perspective: the locked output is taproot and includes a key-spend path on the seller key (or configured multisigTaprootInternalPubKey), so seller can spend unilaterally without marketplace script-path cooperation.
  • The library builds/signs PSBTs but does not broadcast transactions.

Example Transaction Shape (ASCII)

Example values:

  • inscriptionInput.value = 342
  • gasFundingInput.value = 50
  • minRelayFeeRateSatPerVb = 0.1 (default)
  • tx1 estimated size is 189 vB for 2 inputs / 1 output
  • tx1_fee_floor = Math.ceil(189 * 0.1) = 19 sats (choose tx1_fee = 19)
  • lockedValueSats = 342 + 50 - 19 = 373
  • committedOutput.value = 118000 (seller payment UTXO)
  • marketFeeRateBps = 100 -> marketFeeSats = 1180
TX1: Lock Transaction (RBF)
----------------------------------------------------------------
Inputs
  [0] inscriptionInput (seller)                             342
  [1] gasFundingInput (seller, optional)                    50
                                                          -----
                                                           392 in

Outputs
  [0] taproot lock output (2-of-2 script path + key spend) 373

Fee
  19 sats  (>= ceil(vbytes(tx1) * minRelayFeeRateSatPerVb))
TX2 Template: Produced By createProtectedListingFlow
----------------------------------------------------------------
Inputs
  [0] listing input: TX1:0 locked taproot output             373

Outputs
  [0] committedOutput (seller payment UTXO)            118000

Status
  Underfunded by design. Marketplace buy flow adds buyer inputs/outputs.
TX2 Final: Marketplace Buy Assembly (example ordering)
----------------------------------------------------------------
Inputs
  [0] dummy A (p2tr)                                      330
  [1] dummy B (p2tr)                                      330
  [2] listing input (from template, p2tr)                 373
  [3] buyer payment input (p2wpkh)                     120000
                                                        ------
                                                        121033 in

Outputs
  [0] combined dummy (p2tr)                               660
  [1] inscription receive output (user p2tr)              330
  [2] corresponding payment UTXO (seller p2wpkh)       118000   (template output)
  [3] marketplace fee (p2wpkh, optional)                 1180
  [4] buyer change (p2wpkh)                               498
                                                        ------
                                                        120668 out

Fee
  365 sats

Important index rule for seller signature:

  • listing input uses SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, so its corresponding output index must match.
  • in the example, listing input is index 2, so the seller payment output is also index 2.

Computed guidance values returned by the library:

  • marketFeeSats = floor(marketFeeBasisSats * marketFeeRateBps / 10000)
  • prefundingReclaimSats = max(0, lockedValueSats - inscriptionInput.value)