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

sl-bsv

v3.0.0

Published

Bitcoin SV library with a consensus-faithful Script interpreter. Fork of bsv 1.5.6 brought up to Genesis and Chronicle rules, verified against the SV Node vectors.

Readme

sl-bsv

A Bitcoin SV library whose Script interpreter is held to the reference node's behaviour, vector by vector.

npm install sl-bsv

This is a Bitcoin SV library. It tracks BSV consensus as the network runs it — not Bitcoin Core's rules, and not BCH's. What each upgrade changed, and where each rule lives in this codebase, is written up in docs/genesis.md and docs/chronicle.md.

It is a fork of moneybutton/bsv 1.5.6, which was written before Genesis and had drifted two protocol upgrades behind the network. This library brings it forward: Genesis (2020) and Chronicle (activated 7 April 2026 at block 943,816) are both implemented, and the interpreter is measured against the SV Node corpus on every test run.

Status

| | | | --- | --- | | SV Node script vectors (v1.2.2) | 1483 / 1483 | | SV Node transaction vectors | 161 / 161 | | SV Node base58 and key vectors | 112 / 112 | | False accepts (we accept, the node rejects) | 0 | | Transaction digest vectors, both algorithms | 1000 / 1000 | | Result codes matching the node | 600 / 600 rejecting vectors | | Tests | 3959 | | Runtime dependency advisories | 0 | | Coverage | 98% lines, 95% branches | | secp256k1 cross-checked against | OpenSSL | | Opcode table cross-checked against | the BSV wiki |

The corpus is copied verbatim from the node's src/test/data and lives under test/data/bitcoin-sv, with provenance recorded. Every file in it is exercised, on every npm test.

Usage

Keys and addresses

const bsv = require('sl-bsv')

const privateKey = bsv.PrivateKey.fromRandom()
const address = privateKey.toAddress().toString()
const wif = privateKey.toWIF()

Building and signing a transaction

const tx = new bsv.Transaction()
  .from({
    txId: '...',
    outputIndex: 0,
    script: bsv.Script.buildPublicKeyHashOut(publicKey).toHex(),
    satoshis: 100000
  })
  .to(address, 90000)
  .change(address)
  .sign(privateKey)

tx.verify() // true

Running the interpreter directly

The interpreter needs to be told which rules apply, because they depend on when the output being spent was created — not on when you are running the code.

const { Interpreter } = bsv.Script

const flags =
  Interpreter.SCRIPT_UTXO_AFTER_GENESIS |    // the utxo was created post-Genesis
  Interpreter.SCRIPT_UTXO_AFTER_CHRONICLE |  // ...and post-Chronicle
  Interpreter.SCRIPT_CHRONICLE |             // the spending txn is in a Chronicle block
  Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID

const interpreter = new Interpreter()
const ok = interpreter.verify(scriptSig, scriptPubKey, tx, 0, flags, satoshisBN)
if (!ok) console.error(interpreter.errstr) // e.g. 'SCRIPT_ERR_SCRIPTNUM_OVERFLOW'

The UTXO_AFTER_* flags follow the output; SCRIPT_GENESIS and SCRIPT_CHRONICLE follow the spending transaction. The node draws the same distinction, and mixing them up silently changes which rules you are enforcing.

Consensus notes

The full account of each upgrade is in docs/genesis.md and docs/chronicle.md, including where every rule is implemented and what proves it. In brief:

Script numbers are arbitrary precision. Genesis raised the four-byte limit to 750KB and Chronicle to 32MB, so arithmetic on satoshi amounts and on OP_PUSH_TX preimage fields works. They are backed by native BigInt.

// 8-byte satoshi arithmetic, impossible before Genesis
new bsv.Script()
  .add(new bsv.crypto.BN('2100000000000000').toScriptNumBuffer())
  .add(new bsv.crypto.BN(2).toScriptNumBuffer())
  .add(bsv.Opcode.OP_DIV)

Chronicle gates its relaxations on transaction version. A version 1 transaction keeps every malleability rule — MINIMALIF, LOW_S, NULLFAIL, NULLDUMMY, CLEANSTACK, push-only unlocking scripts. They lift only at version 2 and above, which is what makes the upgrade backward compatible.

Genesis stopped P2SH outputs being created, and raised the transaction size limit from 1MB to 1GB. Both are era-dependent, and Transaction#verify takes the era because the object cannot know it:

tx.verify()
// 'transaction txout 0 is pay-to-script-hash, which Genesis disallows'

tx.verify({ afterGenesis: false })  // the pre-2020 rules, for historical txs

The default is the post-Genesis reading, because that is the network a transaction being built now belongs to. A default describing rules the network stopped applying in 2020 would report transactions as valid that no node accepts.

Genesis reverted the locktime opcodes. OP_CHECKLOCKTIMEVERIFY and OP_CHECKSEQUENCEVERIFY are upgradable NOPs for outputs created after Genesis, so a post-Genesis script containing one does not enforce a locktime.

Chronicle restored five opcodes in the old OP_NOP4..OP_NOP8 slots: OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_LSHIFTNUM, OP_RSHIFTNUM. Before Chronicle they still behave as the upgradable NOPs they were. OP_2MUL and OP_2DIV are re-enabled, and OP_VER / OP_VERIF / OP_VERNOTIF became transaction-version operations.

SIGHASH_CHRONICLE (0x20) selects the original digest algorithm instead of BIP143, and both may appear within one transaction.

The opcodes BSV restored in 2018 — OP_MUL, OP_DIV, OP_MOD, OP_INVERT, OP_LSHIFT, OP_RSHIFT, OP_CAT, OP_SPLIT, OP_AND, OP_OR, OP_XOR, OP_NUM2BIN, OP_BIN2NUM — are enabled unconditionally, with no flag to set. OP_2MUL and OP_2DIV are the only opcodes the node disables at all, and only before Chronicle.

Security

No runtime dependency has a known advisory. Two changes in 2.0.0 are the reason:

elliptic is gone. It carried a critical advisory — private key extraction in ECDSA when signing a malformed input — among others. secp256k1 now runs on @noble/curves.

Base58 decoding validates its own characters. base-x 3 looked characters up in a 256-entry table and treated a miss as a sentinel; any character above U+00FF indexed past the end, returned undefined rather than the sentinel, and so skipped the invalid-character branch. A Unicode homograph in an address was caught only by the Base58Check checksum. Base58.decode now rejects anything outside the Bitcoin alphabet before decoding, regardless of what the implementation underneath does.

Reporting a vulnerability: open an issue at github.com/codenlighten/sl-bsv/issues for anything already public, and for anything not yet public please make initial contact privately.

Modules

The library is CommonJS. Both forms work:

const bsv = require('sl-bsv')
import bsv, { PrivateKey, Script } from 'sl-bsv'

The ESM entry point re-exports the same module instance rather than a second build of it. That matters more than it sounds: this library makes 99 instanceof checks, and a dual CommonJS/ESM package would give an importing caller its own copy of every class, so every one of those would silently return false across the two. test/esm-exports.js asserts that identity holds and that the two entry points export the same names.

Note that lib/ is internal. It is reachable, but only with an explicit file extension — require('sl-bsv/lib/crypto/hash.js'), not .../hash.

TypeScript

Type definitions ship with the package and are declared in types, so nothing extra is needed. test/types/consumer.ts compiles the README's examples under strict on every CI run — the definitions this library inherited had gone five major versions stale and described a module name the package no longer used, so they are now held to something.

Browser

Prebuilt bundles are published in the package root: bsv.min.js, and bsv-ecies.min.js, bsv-message.min.js, bsv-mnemonic.min.js, each of which expects bsv.min.js to be loaded first.

Script numbers require BigInt, so the floor is Chrome 67+, Firefox 68+, Safari 14+, Edge 79+. There is no build that supports IE.

Development

npm test                  # lint, unit tests, and the consensus ratchet
npm run test:types        # typecheck a consumer against bsv.d.ts
npm run coverage          # V8 coverage, with a floor
npm run vectors:gap       # how far the interpreter is from the node
npm run build && npm run smoke   # build the bundles and exercise them

All of these run in CI. The suite and the ratchet run on Node 20, 22 and 24; the rest run once, alongside a check that the committed bundles match a fresh build, an audit of the runtime dependencies, an install of the packed tarball into an empty project, and a mutation gate that introduces real defects into lib/ and fails if the suite stays green.

npm test does not cover what webpack emits, so npm run smoke loads each bundle in a bare browser-shaped context and puts it through the operations it exists for. That check exists because a bundle once broke while every test stayed green.

The ratchet

test/consensus/script-vectors.js runs the SV Node corpus on every test run and compares against test/consensus/known-failures.json. That list may only shrink: an unlisted failure is a regression, a listed vector that now passes means the list is stale, and both fail the build. False accepts are held at zero outright rather than through the list, since that is the direction that can cost money.

It also compares the node's result code, exactly — matching the outcome is not the same as failing for the right reason, and the gap between the two had been hiding real bugs.

interpreter.errstr carries the node's code. Where this library can be more specific than the node — an evaluation ending false has four distinguishable causes, all of which the node calls EVAL_FALSE — the narrower reason is on interpreter.errdetail:

interpreter.errstr    // 'SCRIPT_ERR_EVAL_FALSE'
interpreter.errdetail // 'SCRIPT_ERR_EVAL_FALSE_IN_STACK'

Changelog

2.0.0

Breaking. Read the migration notes below before upgrading.

  • Genesis and Chronicle consensus rules implemented; 1483/1483 SV Node script vectors, 0 false accepts.
  • Script numbers are arbitrary precision, backed by native BigInt (750KB post-Genesis, 32MB post-Chronicle).
  • secp256k1 moved from elliptic to @noble/curves. Point addition is ~10x faster and public key derivation ~2x; signature verification is ~17% slower, which is the backend's own cost.
  • Base58.decode rejects characters outside the Bitcoin alphabet.
  • bs58 4 → 5 and bn.js 4.11.9 → 4.12.5, clearing the last advisories.
  • New result codes: SCRIPTNUM_OVERFLOW, SCRIPTNUM_MINENCODE, MOD_BY_ZERO, ILLEGAL_CHRONICLE.
  • Interpreter#errstr now always carries the node's result code; the narrower names this library used to report there moved to Interpreter#errdetail.
  • Transaction#verify applies the post-Genesis rules by default: a pay-to-script-hash output is rejected, and the size limit is 1GB rather than 1MB. Pass { afterGenesis: false } for the earlier rules.
  • SCRIPT_ENABLE_MONOLITH_OPCODES and SCRIPT_ENABLE_MAGNETIC_OPCODES are deprecated no-ops. The opcodes they gated are enabled unconditionally, as they are on the network; setting the flags is harmless and selects nothing.
  • Browser bundles built with webpack 5 and emitted as ES6.

Migrating from 1.x

  • bsv.deps.elliptic is removed. Use bsv.crypto.Point, which keeps the same surface — mul, add, mulAdd, getX, getY — and still returns BN coordinates.
  • Base58.decode now throws Non-base58 character on input it previously accepted and decoded to something arbitrary. Anything relying on the lenient behaviour needs to handle the error.
  • Point.prototype.toBuffer throws for the point at infinity rather than returning a compressed key of 02 and 32 zero bytes.
  • Point.prototype.toString returns <EC Point x: … y: …> where it previously returned [object Object].
  • The interpreter's internal script numbers are BigInt. This is visible if you call Interpreter.prototype.scriptNum, checkLockTime or checkSequence directly; they take and return BigInt, not BN. The public verify / evaluate API is unchanged and the stack is still Buffers.
  • Script#add(number) rejects a number that cannot be an opcode. It takes an opcode, not a value to push — anything outside 0–255 previously built a script that could not be serialised. To push a number, use a Buffer, or BN#toScriptNumBuffer() for a script number.
  • A truncated push is rejected consistently. Previously a script built with one evaluated as valid while the identical bytes parsed from the wire were rejected.
  • Transaction#verify now rejects P2SH outputs by default, because Genesis did. Anything building or checking such an output must either stop or pass { afterGenesis: false } deliberately.
  • Deep imports into lib/ now need the file extension. require('sl-bsv/lib/crypto/hash') becomes require('sl-bsv/lib/crypto/hash.js'), and a directory such as sl-bsv/lib/crypto no longer resolves to its index.js. This is the cost of declaring an exports map, which is what makes import { PrivateKey } from 'sl-bsv' possible at all. The documented entry points — the package root and sl-bsv/ecies, /message, /mnemonic — are unaffected.
  • Node 20.19+ and a BigInt-capable browser are required. The floor comes from @noble/curves 2.x, which this library's EC math rests on.
  • Scripts spending post-Genesis or post-Chronicle outputs need the corresponding SCRIPT_UTXO_AFTER_* flags to be validated correctly. Omitting them means the pre-Genesis rules are enforced.

1.5.0

  • Add build files into repo.

1.4.0

  • Change default fee to 0.5 sat/byte

1.3.0

  • Remove limit on OP_RETURN size

1.1.0

  • Refactor code related to buffers and get rid of bufferUtil
  • Deprecate p2sh
  • Add .Mnemonic to bsv object

1.0.0

  • Bump to 1.0 release as per the suggestion of @mathiasrw

0.30.2

  • Added addSafeData to Transaction.

0.30.1

  • Enforce buffer check for Electrum ECIES decryption.
  • Clean up script folder (no API breaking changes).
  • Documentation improvements.

0.30.0

  • Fix transaction size calculation.

0.29.2

  • Throw error on invalid hex strings in script

0.29.1

  • Add support for new OP_RETURN style: buildSafeDataOut and isSafeDataOut (and getData)

0.27.2

  • Add support for Stress Test Network (STN).

v0.27.1

  • Replace lodash methods with inline pure javascript methods.

v0.27.0

  • Remove version guard. This should fix the "two versions of bsv" error that people often get. Note that it is poor practice to use incompatible versions of bsv. To send objects from one version of the library to another, always serialize to a string or buffer first. Do not send objects from one version to another. This due to frequent use of "instanceof" inside the library.

v0.26.5

  • lodash optimization and overall size optimization of bsv.min.js
  • fix isFinal
  • fix non-dust amount example
  • minor ECIES API issue

v0.26.4

  • Use ECDSA.signWithCalcI(...) convenience method inside Message.

v0.26.3

  • Add ECDSA.signWithCalcI(...) convenience method.

v0.26.2

  • Add Mnemonic.fromString(string).
  • Add convenience method for ECDSA.signRandomK (mostly for demo purposes).
  • Add convenience methods Message.sign and message.verify.
  • Move large portions of the documentation to docs.moneybutton.com.

v0.26.1

  • Add .fromRandom() method for Mnemonic.

v0.26.0

  • Remove the (already deprecated) .derive() method from HDPrivateKey and HDPublicKey. If you rely on this, please switch to .deriveNonCompliantChild(). If you do not already rely on this, you should use .deriveChild() instead.
  • HDPrivateKey / HDPublicKey toHex() and fromHex()
  • HDPrivateKey.fromRandom()
  • Remove Base32 (this was only used for cashaddr and is now obsolete).

v0.25.0

  • Remove support for cashaddr completely. This saves size in the bundle.
  • Private key .toString() method now returns WIF, which makes it compatible with the corresponding .fromString(wif) method.
  • Private key and public key classes now have toHex() and fromHex(hex) methods.

Credits and licence

MIT. This is a fork of bsv by Ryan X. Charles and Money Button, which in turn descends from bitcore-lib by BitPay and from Bitcoin Core. The copyright notices in LICENSE are unchanged.

Consensus behaviour is measured against bitcoin-sv, whose test vectors are redistributed here under the same MIT licence. The corpus is byte-identical between v1.2.0 and v1.2.2, the current release; v1.2.2 changed the interpreter only cosmetically.