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

@xanaverse/contracts

v1.4.0

Published

BSV smart contracts for trustless social media - covenant-enforced posts (lockable v2), upvotes, replies, user identity marketplace, and time-locked payments

Downloads

333

Readme

@xanaverse/contracts

BSV smart contracts for trustless social media. Covenant-enforced posts, upvotes, replies, user identity, and time-locked value, where payment and ownership rules are validated by the Bitcoin network, not platforms.

Install

npm install @xanaverse/contracts

Peer dependency: Requires scrypt-ts ^1.4.0

Quick Start

import {
  PostAnchor,
  PostAnchorArtifact,
  parsePostAnchor,
  PROTOCOL_ID
} from '@xanaverse/contracts'

// Load artifact before using contract
PostAnchor.loadArtifact(PostAnchorArtifact)

// Parse a post from on-chain data
const post = parsePostAnchor(lockingScriptHex)
console.log(post.content, post.creatorIdentityKey)

Source Code

This package includes the raw sCrypt smart contract source files in src/contracts/. You can:

  • Audit the covenant logic that enforces all payment and identity rules
  • Verify artifacts by recompiling with npm run compile
  • Study production covenant patterns (two-tx pattern, commitment privacy, SMT, CLTV timelocks)

Exports

Contract Classes

| Contract | Purpose | |----------|---------| | PostAnchor | Posts with content hash verification and tags (v1, current default) | | PostAnchorV0 | Legacy post format (frozen, parsing/reclaim compatibility only) | | PostAnchorV2 | Lockable posts: CLTV-gated reclaim, committed sats, content modes | | UpvoteProof | Upvote proofs with covenant-enforced creator payments | | ReplyProof | Reply proofs with covenant-enforced parent payments | | DownvoteProof | Downvote proofs (creator still receives payment) | | UserRegistry | SMT-based user number registration (singleton) | | UserProof | Tradeable user number ownership NFT with built-in marketplace | | TimeLockedGift | One-shot time-locked payment covenant (network-enforced unlock date) |

Artifacts

Compiled Bitcoin Script for each contract. Load before instantiating:

import {
  PostAnchorArtifact,
  PostAnchorV0Artifact,
  PostAnchorV2Artifact,
  UpvoteProofArtifact,
  ReplyProofArtifact,
  DownvoteProofArtifact,
  UserRegistryArtifact,
  UserProofArtifact,
  TimeLockedGiftArtifact
} from '@xanaverse/contracts'

PostAnchor.loadArtifact(PostAnchorArtifact)
PostAnchorV2.loadArtifact(PostAnchorV2Artifact)
UpvoteProof.loadArtifact(UpvoteProofArtifact)
UserRegistry.loadArtifact(UserRegistryArtifact)
UserProof.loadArtifact(UserProofArtifact)
TimeLockedGift.loadArtifact(TimeLockedGiftArtifact)

Protocol Constants

import {
  PROTOCOL_ID,
  PROTOCOL_KEY_ID,
  PROTOCOL_COUNTERPARTY,
  PROTOCOL_BASKETS,
  PROTOCOL_SHARED_CONFIG
} from '@xanaverse/contracts'

// BRC-42 key derivation
PROTOCOL_ID           // [0, 'xanaverse']
PROTOCOL_KEY_ID       // '1'
PROTOCOL_COUNTERPARTY // 'self'

// UTXO basket names (BRC-100)
PROTOCOL_BASKETS.posts     // 'xanaverse-posts'
PROTOCOL_BASKETS.upvotes   // 'xanaverse-upvotes'
PROTOCOL_BASKETS.replies   // 'xanaverse-replies'
PROTOCOL_BASKETS.downvotes // 'xanaverse-downvotes'

Post Versions

| Version | Class | Status | Notes | |---------|-------|--------|-------| | v0 | PostAnchorV0 | Frozen | Legacy posts; parsing and reclaim only | | v1 | PostAnchor | Current | Adds tags and version props | | v2 | PostAnchorV2 | Live | Adds lockUntil, contentMode, committedSats |

Parsing Posts (version-aware)

parsePostAnchor detects the version from the script itself (newest first, keyed on the contract's recovered version prop) and returns a normalized shape:

import {
  parsePostAnchor,
  isPostAnchor,
  getPostAnchorInstance
} from '@xanaverse/contracts'
import type { ParsedPostAnchor } from '@xanaverse/contracts'

if (isPostAnchor(lockingScript)) {
  const post: ParsedPostAnchor = parsePostAnchor(lockingScript)

  console.log(post.version)               // 0 | 1 | 2
  console.log(post.content)               // Post text
  console.log(post.creatorProtocolPubKey) // BRC-42 derived key
  console.log(post.creatorIdentityKey)    // Root identity
  console.log(post.contentHash)           // SHA-256 hash
  console.log(post.createdAt)             // Timestamp
  console.log(post.parentHash)            // Parent post (for threads)

  // v1+ only
  console.log(post.tags)                  // Comma-separated tags

  // v2 only (undefined for v0/v1)
  console.log(post.lockUntil)             // Unix-time CLTV lock; 0n = unlocked
  console.log(post.contentMode)           // 1n full | 2n hash-only | 3n hash+data
}

// Get raw contract instance (for advanced operations like reclaim).
// v2 posts MUST reclaim via the v2 instance: its reclaim() carries the
// CLTV gate and pays out committedSats.
const { version, contract } = getPostAnchorInstance(lockingScript)

Resolving Engagement Proof Instances

Version-aware instance resolution for upvotes, downvotes, and replies (used to dispatch reclaims). All engagement protocols are currently v1:

import {
  getUpvoteProofInstance,
  getDownvoteProofInstance,
  getReplyProofInstance
} from '@xanaverse/contracts'

const { version, contract } = getUpvoteProofInstance(lockingScript)
await contract.methods.reclaim(...)

Contract Properties

PostAnchor (v1)

interface PostAnchorState {
  creatorProtocolPubKey: PubKey    // BRC-42 derived identity
  creatorIdentityKey: ByteString   // Root identity (display name)
  content: ByteString              // Post text (max 280 chars)
  contentHash: ByteString          // SHA-256 of content
  nonce: ByteString                // Privacy nonce
  createdAt: bigint                // Unix timestamp
  parentHash: ByteString           // Parent post hash (threads)
  tags: ByteString                 // Comma-separated tags
  targetDifficulty: bigint         // PoW target (immutable)
  version: bigint                  // Contract version
}

PostAnchorV2 (lockable posts + content modes)

Everything in v1, plus three stateful props that the covenant binds forward unchanged from deploy:

interface PostAnchorV2Extras {
  lockUntil: bigint      // Unix-time lock; 0n = unlocked
  contentMode: bigint    // 1n full | 2n hash-only | 3n hash+data
  committedSats: bigint  // Sats frozen in the anchor until lockUntil
}

// Content mode constants
PostAnchorV2.MODE_FULL      // 1n - body on-chain at publish (v1 behaviour)
PostAnchorV2.MODE_HASH_ONLY // 2n - only contentHash on-chain; body off-chain
PostAnchorV2.MODE_HASH_DATA // 3n - contentHash at publish, body via addContent (3rd tx)

Methods:

  • publish(sig, creatorIdentityKey, content, tags, contentHash, createdAt, parentHash) reveals the post and rebuilds the anchor at committedSats. The post is visible immediately; only the sats are frozen.
  • addContent(sig, content) (mode 3 only) reveals the body in a third transaction and proves sha256(content) == contentHash on-chain.
  • reclaim(sigCreator) is CLTV-gated: the covenant asserts this.timeLock(lockUntil), so the spending transaction must carry nLockTime >= lockUntil with a non-final input sequence, and the network refuses to confirm it early. Pays committedSats back to the creator.

How the timelock works on BSV: OP_CHECKLOCKTIMEVERIFY is a NOP on BSV post-Genesis (Feb 2020). What Genesis kept is consensus enforcement of transaction finality: a tx with a future nLockTime and a non-final input sequence will not be mined until median-time-past passes the lock. The covenant's timeLock() assertion reads the spending tx's own nLockTime/nSequence from the sighash preimage and forces the spender to build a tx the network refuses to confirm early.

TimeLockedGift

A standalone, one-shot time-locked payment covenant. Pay someone such that they fully own the claiming key, but the network itself refuses to let them spend the coins until lockUntil (same timeLock() mechanism as PostAnchorV2 above).

interface TimeLockedGiftState {
  recipient: PubKey   // 33-byte compressed pubkey; their key authorizes the claim
  lockUntil: bigint   // Unix timestamp (>= 500000000) before which coins can't move
  amount: bigint      // Exact sats paid to the recipient on unlock
}

Method: unlock(sig) asserts the timelock has elapsed, checks the recipient's signature, and pins the output to P2PKH(recipient, amount). The spending tx-builder must set nLockTime >= lockUntil and a non-final input sequence (e.g. 0xfffffffe).

import { TimeLockedGift, TimeLockedGiftArtifact } from '@xanaverse/contracts'

TimeLockedGift.loadArtifact(TimeLockedGiftArtifact)

const gift = new TimeLockedGift(
  PubKey(recipientPubKeyHex),
  BigInt(unlockUnixTime),
  BigInt(satoshis)
)

UpvoteProof

interface UpvoteProofState {
  upvoterProtocolPubKey: PubKey    // Who upvoted (BRC-42)
  upvoterIdentityKey: ByteString   // Upvoter display name
  contentHash: ByteString          // Post being upvoted
  creatorProtocolPubKey: PubKey    // Post creator (payment recipient)
  creatorIdentityKey: ByteString   // Creator display name
  paymentAmount: bigint            // Sats paid to creator (min 100)
  nonce: ByteString                // Privacy nonce
}

ReplyProof

interface ReplyProofState {
  replierPubKey: PubKey            // Who replied (BRC-42)
  replier: ByteString              // Replier display name
  replyContent: ByteString         // Reply text
  replyContentHash: ByteString     // SHA-256 of reply
  parentContentHash: ByteString    // Parent being replied to
  parentProtocolPubKey: PubKey     // Parent creator (payment recipient)
  parentIdentityKey: ByteString    // Parent creator display name
  paymentAmount: bigint            // Sats paid to parent (min 50)
  createdAt: bigint                // Unix timestamp
  nonce: ByteString                // Privacy nonce
}

UserRegistry (Singleton)

interface UserRegistryState {
  nextUserId: bigint               // Next sequential ID to assign
  registeredKeysRoot: ByteString   // SMT root of registered identities
  treasuryAddress: PubKeyHash      // Registration fee recipient
  platformAddress: PubKeyHash      // Marketplace fee recipient
}

// Constants
UserRegistry.REGISTRATION_FEE = 1000n  // satoshis
UserRegistry.SMT_DEPTH = 256           // full sha256 keyspace

UserProof (Tradeable NFT)

interface UserProofState {
  // Immutable (provenance)
  userId: bigint                   // Assigned user number (never changes)
  originalRegistrant: PubKey       // First registrant

  // Mutable (ownership)
  ownerPubKey: PubKey              // Current owner's protocol key
  ownerIdentityKey: PubKey         // Current owner's identity key
  isForSale: boolean               // Listing status
  salePrice: bigint                // Price in satoshis

  // Platform
  platformAddress: PubKeyHash      // Marketplace fee recipient
}

// Constants
UserProof.PLATFORM_FEE_BPS = 100n  // 1% marketplace fee

Marketplace methods (all covenant-enforced, owner signature first):

  • list(sig, price) - list the user number for sale
  • updatePrice(sig, newPrice) - change the asking price
  • delist(sig) - remove the listing
  • buy(sig, newOwner, newOwnerIdentityKey) - purchase; covenant validates the seller payout, tx builder adds the 1% platform fee output
  • transfer(sig, newOwner, newOwnerIdentityKey) - direct transfer, no payment
  • reclaim(sig) - burn the UTXO

Usage Examples

Parse Contract from Transaction Output

import { PostAnchor, PostAnchorArtifact } from '@xanaverse/contracts'

// Load artifact once at startup
PostAnchor.loadArtifact(PostAnchorArtifact)

// Parse from locking script
const post = PostAnchor.fromLockingScript(lockingScriptHex)
console.log(post.content.toString())
console.log(post.creatorProtocolPubKey.toString())

Derive Earnings from Proof UTXOs

import { UpvoteProof, ReplyProof } from '@xanaverse/contracts'

// Parse actual payment amounts (don't estimate!)
let totalEarnings = 0n

for (const utxo of upvoteUTXOs) {
  const proof = UpvoteProof.fromLockingScript(utxo.script)
  totalEarnings += proof.paymentAmount  // Could be 100, 150, 200+ sats
}

for (const utxo of replyUTXOs) {
  const proof = ReplyProof.fromLockingScript(utxo.script)
  totalEarnings += proof.paymentAmount  // Could be 50, 75, 100+ sats
}

Use Protocol Constants for Wallet Integration

import { PROTOCOL_SHARED_CONFIG } from '@xanaverse/contracts'

// Get XanaVerse protocol key (BRC-42)
const { publicKey } = await wallet.getPublicKey({
  protocolID: PROTOCOL_SHARED_CONFIG.protocolID,
  keyID: PROTOCOL_SHARED_CONFIG.keyID,
  counterparty: PROTOCOL_SHARED_CONFIG.counterparty
})

Parse User Registry State

import { UserRegistry, UserRegistryArtifact } from '@xanaverse/contracts'

UserRegistry.loadArtifact(UserRegistryArtifact)

const registry = UserRegistry.fromLockingScript(lockingScriptHex)
console.log(`Users registered: ${registry.nextUserId - 1n}`)
console.log(`SMT root: ${registry.registeredKeysRoot}`)

Parse User Proof (NFT)

import { UserProof, UserProofArtifact } from '@xanaverse/contracts'

UserProof.loadArtifact(UserProofArtifact)

const proof = UserProof.fromLockingScript(lockingScriptHex)
console.log(`User #${proof.userId}`)
console.log(`Owner: ${proof.ownerIdentityKey}`)
console.log(`For sale: ${proof.isForSale} @ ${proof.salePrice} sats`)
console.log(`Original registrant: ${proof.originalRegistrant}`)

TypeScript Types

import type { ParsedPostAnchor } from '@xanaverse/contracts'

// Full type definitions included for all exports

Architecture

These contracts implement the XanaVerse trustless social media protocol:

Content Layer:

  • Posts create immutable content anchors with cryptographic authorship
  • v2 posts can additionally freeze sats in the anchor until a network-enforced unlock time, and choose how much content lives on-chain (full / hash-only / hash+deferred-reveal)

Engagement Layer:

  • Upvotes create proof UTXOs with covenant-enforced creator payments
  • Downvotes create proof UTXOs (creator still receives payment)
  • Replies create proof UTXOs with covenant-enforced parent payments

Identity Layer:

  • UserRegistry singleton manages sequential user number assignment with SMT duplicate prevention
  • UserProofs are tradeable NFTs representing user number ownership, with a built-in covenant marketplace (list / buy / transfer)

Value Layer:

  • TimeLockedGift pays sats that the recipient owns but cannot move until a network-enforced unlock time

Trust Model:

  • Counts are derived by querying proof UTXOs (indexers can't fake them)
  • All payment rules are enforced by Bitcoin Script at the network level
  • User identities are cryptographically provable via Sparse Merkle Tree
  • Timelocks are enforced by transaction finality consensus, not application code

Related

License

MIT