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

@terlos/stacks-client

v0.5.0

Published

Stacks Shield wallet client: private wallets and accounts under one password, root-checked sync, on-device STARK proving, shield / pay / withdraw / split / merge through relayers with automatic re-proving, payment requests. Framework-free; runs in a Web W

Readme

@terlos/stacks-client

npm License: Apache-2.0 Status

Part of Terlos: private payments on Stacks with ZK-STARKs verified on chain. Architecture · Integration guide · Security

The high-level wallet client for Terlos, a zk-STARK shielded pool on Stacks. Use it to give users private balances in STX, sBTC and other SIP-010 tokens, to pay privately, and to ask to be paid with payment requests.

One object does everything:

  • Wallets: several private wallets and accounts, all under one password.
  • Sync: checked against the chain's root.
  • Proving: STARK proofs are made on the device.
  • Operations: shield, pay, withdraw, split and merge, submitted through relayers, with automatic re-proving when another user's transaction lands first.

It is framework-free and runs in Node or the browser, ideally inside a Web Worker.

Status: 0.x, testnet, unaudited. APIs may change between minor versions. Don't use it with mainnet funds until the protocol's external audit is done.

npm install @terlos/stacks-client

Quick start (browser, in a Web Worker)

// shield.worker.ts
import "@terlos/stacks-client/worker";
// app
import { wrap, proxy } from "comlink";
import type { CoreApi } from "@terlos/stacks-client";
import engineUrl from "@terlos/stacks-sdk/wasm/stacks_shield_wasm.wasm?url";        // Vite
import engineSha256 from "@terlos/stacks-sdk/wasm/stacks_shield_wasm.sha256?raw";

const shield = wrap<CoreApi>(new Worker(new URL("./shield.worker.ts", import.meta.url), { type: "module" }));
import { TESTNET } from "@terlos/stacks-client";               // or MAINNET

await shield.init(
  {
    ...TESTNET,                                             // network, deployer, stacksApi
    indexerUrl: "https://indexer.example",                  // see "Services" below
    relayerUrls: ["https://relayer-1.example", "https://relayer-2.example"],
  },
  new URL(engineUrl, location.href).href,
  engineSha256.trim(),   // the prover engine is refused unless its SHA-256 matches
);

// a new private wallet (24 words: show them to the user to back up)
const words = await shield.generateWords();
await shield.createWallet(words, "a strong password");

await shield.sync();
const balances = await shield.balances();        // { stx: 12500000n, "1": 300n, ... } base units
const me = await shield.address();               // zst1… (testnet) / zsp1… (mainnet)

// pay 2.5 STX privately to a shielded address (notes are merged / split automatically)
await shield.pay("zst1…", 2_500_000n, null, proxy((step) => console.log(step)));

In Node, create a ShieldClient directly:

import { readFileSync } from "node:fs";
import { ShieldClient, memoryStore } from "@terlos/stacks-client";

const file = (p: string) => readFileSync(new URL(import.meta.resolve(`@terlos/stacks-sdk/wasm/${p}`)));
const wasm = file("stacks_shield_wasm.wasm");
const bytes = wasm.buffer.slice(wasm.byteOffset, wasm.byteOffset + wasm.byteLength);
const sha256 = file("stacks_shield_wasm.sha256").toString("utf8").trim();
const client = await ShieldClient.create(config, { bytes, sha256 }, memoryStore());

Pass your own KeyValueStore (get / set / remove) to persist state. idbStore() uses IndexedDB in browsers. Everything it stores is sealed: the backup words with the password (PBKDF2-SHA256, 600,000 iterations, then AES-256-GCM), history and labels with a key derived from the words.

Testnet and mainnet

The same code runs on both networks; the deployment presets give the right contract addresses:

| Preset | Network | Deployment | | --- | --- | --- | | TESTNET | testnet | v5, ST2RD49DFQ4PM58SMR571CTPFF8JKXFA3FFJA22GK: the same contract code as mainnet | | MAINNET | mainnet | v1, SP2G6D6AX0WJ4JPK8PK6EBW8QQXBGMWTXE4J0VABX: real funds, unaudited, deposits capped | | TESTNET_V4 | testnet | the previous testnet deployment, for apps still on it |

DEPLOYMENTS[network] picks the current one by name. Test on TESTNET first: it behaves exactly like mainnet, with free test funds. Each network needs its own indexer and relayers.

Wallets are separate per network. The same 24 words give different keys on mainnet and on testnet, so a person's testnet and mainnet addresses (zst1… / zsp1…) cannot be linked. A backup restores the wallet on whichever network the client is configured for.

API overview

| Area | Methods | | --- | --- | | Wallets | status, generateWords, validWords, createWallet, unlock, lock, addWallet, addAccount, switchAccount, renameWallet, renameAccount, revealWords, removeWallet, forget, wallets | | Addresses | address (primary), newAddress (a fresh one per counterparty), addresses, setAddressLabel | | State | sync, balances, notes, history, assets, stats, depositRoom, withdrawRoom | | Operations | shield(amount, asset, sender, signer) (deposit, signed by the user's Stacks wallet), pay(to, amount, asset), send(note, to), split, merge, withdraw(note, recipient) | | Payment requests | createPaymentRequest({ amount, asset, id?, label?, message?, expiresIn? }) → { request, uri, watchKey, fromLeaf }, readPaymentRequest(uri), payRequest(uri) | | Relayers | quote(): the fees of the relayer the next operation uses, never above the signed fee table |

Amounts are bigint base units (micro-STX, satoshis for sBTC). Assets are null for STX, or the asset's uid in the protocol's asset registry (sBTC = 1, USDCx = 2 on testnet v4).

Operations accept a progress(step, detail?) callback. The steps are syncing, proving, signing, submitting, confirming and retrying. retrying means another transaction landed first and the client is proving again against the new state, automatically.

Accepting payments (merchants)

const { uri, watchKey, fromLeaf, request } = await client.createPaymentRequest({
  amount: 2_500_000n, asset: null, id: "order-42", label: "Coffee Shop", expiresIn: 3600,
});
// show `uri` as a QR code or link it (paymentLink in @terlos/stacks-pay);
// give `watchKey` + `fromLeaf` to your server's PaymentWatcher to confirm it

Each request uses a fresh receiving address, so payments can't be linked to each other, and the watch key reveals only that one address's incoming notes. See @terlos/stacks-pay.

Services

The client talks to:

  • a Stacks API (Hiro or your own node): the source of truth for the root, spends and outcomes;
  • an indexer (services/indexer): bulk tree and memo data, always re-checked against the chain;
  • one or more relayers (services/relayer): they submit private operations, so the user's Stacks address never appears. Relayers are used round-robin, with failover only for refusals another relayer can accept, never at a higher fee than shown.

Public hosted endpoints will be announced. Until then, run the services yourself (see the protocol repository's services/ and scripts/testnet/services.ts).

Relayer fees: the signed fee table

Relayer fees come from a table signed by the protocol's fee admin (see @terlos/stacks-sdk/relayer-fees). The client handles it as follows:

  1. Find the table in force. It reads GET /v1/fees from every configured relayer, and keeps the newest table that verifies: signed by a wallet that holds the fee-admin role on chain now, and made for your deployment. A relayer serving an older or forged table can't raise what users pay.
  2. Cap the fees. It uses a relayer only if its quote, for the operation and asset, is at or below that table.
  3. Refuse without a table. Until a table is published, relayed operations (pay, send, split, merge, withdraw) are refused with "No relayer fee schedule is in force yet". Deposits don't need a relayer and always work.

For a new deployment, the fee admin publishes the table once, from the governance console's Relayer fees page. Relayers that share a database keep it across restarts.

Withdrawals are also submitted with the checked protocol fee as their max-fee, so a protocol fee raised before the transaction lands aborts it instead of charging more.

Security notes

  • Keys never leave the client. In the browser, run it in a Web Worker so the page never holds keys.
  • The prover engine is pinned by SHA-256 and refused if it differs.
  • The client never asks a node about individual notes. Spends are synced in bulk, so nobody learns which notes are yours.
  • Show users their 24 backup words and make them confirm the backup. Clearing browser storage without them loses the funds.

License: Apache-2.0.