Skip to content

summraznboi/mempool-protection

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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)

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors