Stacks Shield — Encrypted zero-knowledge for private peer-to-peer payments on Stacks

Privacy has always been an interesting problem on public blockchains because transparency is one of the things that makes them useful in the first place.

On Stacks, transaction history, balances, and addresses are publicly observable. That is valuable for verification, but there are applications where exposing the complete financial relationship between addresses is not desirable.

That question is what led to Stacks Shield.

Stacks Shield is an open-source privacy protocol for Stacks that uses zkSNARKs to allow users to move STX and SIP-10 assets through shielded notes while keeping the underlying note ownership and amounts private.

What started as an exploration of private STX transactions has now grown into a multi-asset privacy system supporting STX, sBTC, and USDCx on Stacks Testnet, with a published developer SDK.

This post introduces what we built, why we built it, how the system works, and where we want to take it next.


From private STX to a multi-asset privacy layer

The first version of Stacks Shield was designed around native STX.

The basic model is straightforward:

  1. A user deposits STX into a privacy pool.

  2. The deposit becomes a shielded note represented publicly by a cryptographic commitment.

  3. The user’s wallet keeps the information required to spend that note.

  4. When the user wants to move the funds, a zkSNARK proves that they are authorized to spend a valid note without revealing the note’s private information.

  5. A relayer submits the resulting transaction.

  6. The recipient receives a new shielded note.

  7. The funds can eventually be withdrawn to a transparent Stacks address.

The public chain can verify that the operation is valid without learning the private details contained inside the note.

That foundation worked well enough to raise the next question:

Could the same privacy infrastructure support assets beyond STX?

That is where SIP-10 support came in.


Adding sBTC and USDCx without fragmenting privacy

The goal was not to create a separate privacy protocol for every token.

We wanted one system that could support multiple assets while preserving the existing STX implementation.

The design therefore treats SIP-10 support as an additive extension to the existing STX privacy core. The native STX protocol remains frozen, while the SIP-10 layer reuses shared components such as the privacy registry and note infrastructure.

Today, the testnet deployment supports:

  • STX

  • sBTC

  • USDCx

And the architecture is designed around registered SIP-10 assets rather than hard-coded token implementations.

The on-chain asset-registry is the source of truth for supported assets. The API exposes that registry through asset discovery, and the SDK uses the resulting metadata to route operations to the appropriate pool and verifier. Adding another registered SIP-10 asset therefore does not require creating another SDK implementation.

This was an important architectural decision.

We wanted:

One privacy system, many assets.


How privacy works

At the heart of Stacks Shield is the concept of a shielded note.

A note contains information such as:

  • amount

  • owner keys

  • blinding value

  • asset information where applicable

The blockchain does not store that information in plaintext as the representation of the user’s private balance.

Instead, the note is represented publicly by a cryptographic commitment.

For native STX, the commitment is based on:

Poseidon4(amount, ownerPkX, ownerPkY, blinding)

For SIP-10 assets, the asset itself becomes part of the cryptographic commitment:

Poseidon2(
    Poseidon4(amount, ownerPkX, ownerPkY, blinding),
    asset_id
)

where asset_id is derived from the token contract principal.

This matters because sBTC and USDCx can share the same underlying commitment infrastructure without becoming interchangeable.

A USDCx note cannot simply be treated as an sBTC note because the asset is cryptographically bound to the commitment.


One shared Merkle tree

Another deliberate design choice was to avoid creating a separate anonymity infrastructure for every asset.

All commitments are stored in a shared commitment tree.

That means STX, sBTC and USDCx notes can participate in the same underlying commitment infrastructure while their asset identities remain cryptographically bound.

The privacy model also uses nullifiers.

When a note is spent, its nullifier is published. The registry can therefore reject a second attempt to spend the same note without revealing which note was spent.

This gives us the basic privacy primitives required for the shielded lifecycle:

commitments → Merkle membership → zkSNARK proofs → nullifiers → new commitments


What actually happens during a private transaction?

Consider Alice and Bob.

Alice has sBTC in a transparent wallet.

She shields it through STX Shield.

Her wallet generates the note information and creates the corresponding commitment. The proof is generated client-side using the proving engine. The encrypted note information can be published as opaque ciphertext, while the information needed to interpret the note remains with the user’s wallet.

Alice can then:

Shield → Transfer → Split → Merge → Withdraw

For a private transfer to Bob, Alice’s wallet generates a proof demonstrating that:

  • the input note exists,

  • she can spend it,

  • the Merkle path is valid,

  • the nullifier is valid,

  • the resulting commitments satisfy the circuit’s rules,

  • and, for SIP-10, the asset remains the same.

The proof does not reveal the private note information.

The transaction is then submitted through the relayer rather than directly from Alice’s wallet. The relayer constructs and broadcasts the on-chain transaction, meaning the Stacks transaction does not identify Alice as the sender of the private operation.

Bob receives a new shielded note.

Later, Bob can withdraw the asset to a different transparent address.

The resulting public transaction shows the withdrawal, but the privacy protocol does not provide a public link back to the original shielded note.


Where zkSNARKs and zkVerify fit

The proof system is built around Noir and UltraHonk circuits.

There are circuits for:

  • shield

  • transfer

  • split

  • merge

  • withdraw

with native STX and SIP-10 variants. SIP-10 circuits additionally bind the asset_id into the public inputs and commitments.

Proofs are generated client-side through @aztec/bb.js.

The next challenge is verification.

UltraHonk proofs are currently too expensive to verify directly inside Clarity, and Clarity does not currently provide the elliptic-curve pairing primitives required for this style of direct SNARK verification.

Stacks Shield therefore uses zkVerify as the current verification and aggregation layer.

The flow is:

Private witness
      ↓
zkSNARK circuit
      ↓
Proof generated
      ↓
zkVerify
      ↓
Proof verification + aggregation
      ↓
Aggregation root
      ↓
Stacks
      ↓
On-chain membership verification

zkVerify verifies proofs and aggregates them into an aggregation root. The relayer publishes that root on Stacks. The Stacks contracts then only need to verify that the relevant public-input leaf belongs to a valid aggregation root, rather than executing the full SNARK verification themselves.

This is an important part of the current architecture, and also one of the areas we want to keep improving.


The API and Relayer

The privacy protocol is not just a collection of Clarity contracts.

There is a complete backend layer supporting the SDK.

The API acts as the canonical source for protocol metadata and indexed chain information.

It indexes:

  • commitments

  • encrypted notes

  • aggregation roots

  • protocol statistics

  • registered assets

  • relevant contract events

Importantly, the API does not store note amounts, note secrets, or mappings between nullifiers and commitments. The note feed contains opaque encrypted data and public locators.

The relayer handles the execution side.

It validates proof metadata, submits proofs for verification, tracks aggregation, publishes aggregation roots, and constructs the eventual Stacks transaction.

This separation lets the SDK remain relatively simple for developers.


The SDK

The next step after getting the protocol working was making it usable.

We did not want developers integrating with:

  • Clarity contracts

  • Merkle trees

  • proof generation

  • zkVerify

  • nullifiers

  • asset registries

  • relayer APIs

individually.

The SDK is intended to be the integration surface.

The published package is:

npm install @stacks-shield/sdk

A developer can work with the protocol through a unified interface while the SDK handles the underlying routing.

For example:

const shield = new STXShield({
  network: "testnet",
  signer,
  proofEngine,
  noteVault,
});

await shield.shield(100, "USDCx");

The SDK contains injectable interfaces for the proving engine, wallet signer, API provider, relayer provider, zkVerify integration and local note storage.

The objective is simple: privacy should be something an application can integrate rather than rebuild.


Testnet: the part we cared about most

A privacy protocol can look convincing in a diagram and still fail when real components are connected.

We wanted to get past that point.

Stacks Shield is currently running on Stacks Testnet, and the multi-asset implementation has been exercised against the real testnet sBTC and USDCx contracts rather than local mock tokens.

We validated the complete lifecycle for:

STX

Shield
  ↓
Scan
  ↓
Transfer
  ↓
Split
  ↓
Merge
  ↓
Withdraw

USDCx

The same complete private lifecycle was exercised using the real SIP-10 token.

sBTC

The same lifecycle was exercised with real testnet sBTC, including a larger-value test and withdrawal to a new transparent address.

Across the three assets, the SDK-level validation covered:

  • shielding

  • note discovery

  • private transfers

  • splitting

  • merging

  • withdrawals

  • replay rejection

  • value conservation

  • asset-aware routing

The project documentation records the reusable all-asset SDK test suite for STX, USDCx and sBTC.

This is the milestone that matters most to us: the architecture isn’t only deployed; the full path has been exercised end-to-end.


What happens to the original STX implementation?

Nothing.

That was one of our strict design requirements.

The native STX protocol was deployed as a frozen core.

SIP-10 support was introduced as an additive extension rather than modifying the existing STX contracts. This means the original STX behaviour remains intact while the new SIP-10 layer introduces multi-asset privacy capabilities.

That gives the system two important properties:

Backward compatibility

Existing STX notes and flows continue to work.

Extensibility

Future SIP-10 assets can use the same privacy infrastructure through registration rather than requiring another protocol implementation.


What we learned building it

One of the most useful lessons from building STX Shield has been that the hard part isn’t a single cryptographic primitive.

It is making all of the pieces agree.

The system crosses several boundaries:

Wallet
  ↓
SDK
  ↓
Proof Engine
  ↓
API / Relayer
  ↓
zkVerify
  ↓
Aggregation
  ↓
Clarity
  ↓
Stacks

A failure in indexing can make a perfectly valid note appear missing.

A stale Merkle tree can make a valid proof unusable.

An incorrectly routed verifier can make a valid SIP-10 proof fail.

A missing asset binding can undermine isolation between tokens.

A relayer error can break an otherwise correct proof flow.

Live testnet validation exposed several of these integration problems and forced us to test the protocol as a system rather than as isolated components.

That has probably been one of the most valuable parts of the project.


Privacy needs an anonymity set

There is also a broader question that we think deserves more attention on Stacks:

How useful is a shielded pool if very few people use it?

Zero-knowledge proofs can hide the relationship between a note and a transaction, but privacy is not created by mathematics alone.

The size and activity of the anonymity set matter.

A pool with many users, many notes and frequent transactions gives observers a much harder correlation problem than a pool with only a handful of participants.

That is why STX Shield is being designed as reusable infrastructure rather than a privacy feature tied to one application.

The long-term goal is to make shielded infrastructure available to more wallets, applications, payment flows and DeFi protocols across Stacks.

More legitimate activity in the same privacy infrastructure means a stronger anonymity set.


Where we go from here

The current testnet milestone is not the finish line.

There are several areas we want to continue working on.

Security

The protocol needs serious external security review before any mainnet deployment.

That includes the Clarity contracts, circuits, SDK, relayer and surrounding infrastructure.

More integrations

Privacy becomes considerably more useful when applications can integrate it directly.

The SDK is therefore an important part of the next stage.

More assets

The SIP-10 architecture was intentionally designed around registry-driven asset support.

The goal is to make adding another compatible asset primarily an on-chain registration process rather than another round of protocol development.

Decentralizing execution

The relayer currently provides an important part of the transaction flow.

Longer term, we want to investigate ways to make execution more decentralized while preserving the privacy properties of the system.

Reducing external verification dependencies

Today, zkVerify is an important part of the architecture.

That is useful for getting a working system running, but we don’t consider an external verification layer the final destination.

One direction we are interested in is self-hosted proof verification and aggregation.

The longer-term question is even more interesting:

What would it take for Stacks to verify ZK proofs natively?

That could involve changes such as pairing-friendly cryptographic primitives or other Clarity-compatible verification mechanisms. The current architecture makes this a concrete engineering and ecosystem question rather than a theoretical one.


Why we are building this

The broader idea behind Stacks Shield is bigger than private transfers.

Stacks is increasingly becoming a place where Bitcoin-backed assets and applications can be used in more sophisticated ways.

As that happens, privacy becomes an important part of the application layer.

Users may want to:

  • move assets without exposing their entire transaction history,

  • receive payments privately,

  • manage treasury operations,

  • interact with DeFi without revealing every position,

  • transfer value between shielded identities,

  • or simply have more control over what their financial activity reveals publicly.

Applications should not have to build a new privacy protocol every time they need one of these capabilities.

That’s the problem we’re trying to address.


STX Shield is now open

STX Shield is now publicly available as an open-source project.

GitHub

Repository:

SDK

npm:
@stacks-shield/sdk

Install it with:

npm install @stacks-shield/sdk

Testnet

Stacks Shield Testnet:

The current implementation supports STX, sBTC and USDCx, with the complete shielded lifecycle available on Stacks Testnet.


Closing

Stacks Shield started with a simple question:

Can we build useful zero-knowledge privacy infrastructure directly around the Stacks ecosystem?

The answer we have today is a working testnet implementation.

It generates zkSNARK proofs client-side, uses zkVerify for verification and aggregation, settles through Clarity contracts, routes multiple assets through a common privacy architecture, and exposes the system through a developer SDK. The testnet implementation has now been exercised across native STX, sBTC and USDCx.

There is still a lot to prove before this should be considered production-ready.

Security reviews, broader adoption, stronger anonymity sets, decentralized infrastructure, and eventually a path toward more native ZK verification on Stacks are all part of the work ahead.

But the foundation is now public.

STX Shield is live on Stacks Testnet, the SDK is published, and the code is open for developers to inspect, test, build with, and challenge.

If you’re building on Stacks and are interested in zero-knowledge, privacy, sBTC, SIP-10, or ZK infrastructure, we’d love to have you take a look.


Links


STX Shield is currently a testnet project. The implementation should be treated accordingly until further security review and mainnet readiness work are completed.

Congrats, really interesting work.

I’ve been working on drafting BCYX, which touches a related privacy space but from a different angle:
https://github.com/bcyx-protocol/docs

From what I understand, Stacks Shield is more focused on private payments and shielded assets directly on Stacks, while BCYX is more about privacy-preserving cross-chain coordination and selective disclosure between different participants and networks.

Both seem to share some similar primitives around commitments, nullifiers and ZK proofs, but the scope is quite different.

One thing I’m also curious about: do you see any role for TEEs here, maybe for proving or coordination on resource-constrained clients, while keeping ZK as the actual verification layer?

Nice work, looking forward to seeing how it evolves.

Stacks Shield security log

This is a short log of the security work on Stacks Shield over the past week. I run

these as internal reviews and write them up so the record stays public. Stacks Shield

is a proof of concept privacy protocol on Stacks. It lets people hold and move STX and

SIP-10 tokens like sBTC and USDCx as shielded notes instead of transparent balances,

using zero-knowledge proofs.

What the review covered

I went through the eleven Clarity contracts, the ten Noir circuits, and the SDK cryptography, tracing value and authority flows against a shielded-pool checklist.

What it found

One critical issue. The Merkle tree’s new root was not bound by any proof, so a

leaf-adding operation could advertise a root that was never actually computed. On a

shielded pool that is a path to draining funds. This one is fixed.

One high issue that is still open, and I want to be plain about it. The zkVerify

aggregation root is posted on chain by an authorized relayer, and the only on-chain

check today is that the publisher is authorized. That makes root publication a trusted

role, not a trustless one. It runs through a small dedicated relayer set rather than

the deployer key, and the real fix is a threshold scheme where several independent

operators co-sign each root. Until that ships, I do not call root publication

trustless.

Two minor issues, both fixed, and one informational note about a key-derivation step

that I will change at the next version bump.

What shipped

The critical fix is live as v2 on testnet. Every leaf-adding operation now binds the

tree transition into the proof, the new root and the exact insertion slot, and each

pool asserts the on-chain slot matches the proof. The full lifecycle, shield,

transfer, split, merge, and withdraw, was validated end to end on testnet with real

proofs for STX, sBTC, and USDCx.

Try it

The testnet app is live at https://shield-stx.alkebulant.com/. Shield some testnet STX

or sBTC, then run a transfer, split, or withdrawal and watch it settle. Use test funds

only, this is testnet.

Status

Testnet only. This is an internal review by me, not an independent third-party audit,

and an external audit is required before any mainnet use. The high finding above is

open by design and scheduled. If you build on this, treat it as a testnet experiment.

Read the details

- Security review: stacks-shield/audits/security-review.md at main · cypherpulse/stacks-shield · GitHub

- White paper: stacks-shield/docs/whitepaper.md at main · cypherpulse/stacks-shield · GitHub

- Yellow paper (spec): stacks-shield/docs/yellowpaper.pdf at main · cypherpulse/stacks-shield · GitHub

- Security model: stacks-shield/docs/security.md at main · cypherpulse/stacks-shield · GitHub

- Changelog: stacks-shield/CHANGELOG.md at main · cypherpulse/stacks-shield · GitHub

- Code: GitHub - cypherpulse/stacks-shield: Privacy infrastructure for Stacks, enabling private transactions across STX and SIP-10 assets. · GitHub

If you work on ZK or Clarity and want to poke holes in this, the review and the code are open. I would rather hear about a problem now than after mainnet.

Thanks, really appreciate this. I took a look at the BCYX direction, and I think the distinction you made is pretty accurate.

Stacks Shield is currently focused on the private payment layer on Stacks shielded notes, asset-bound commitments, nullifiers, Merkle membership and zkSNARK proofs for STX and SIP-10 assets such as sBTC and USDCx. BCYX seems to be approaching privacy from a broader cross-chain coordination and selective-disclosure perspective, so there is definitely some interesting overlap in the underlying primitives while solving different problems.

On TEEs, I do think there could be a role for them, particularly around coordination, proving infrastructure, or reducing the computational burden on constrained clients. But I would be careful about making the TEE part of the privacy trust model. My preference would be to keep ZK as the cryptographic verification layer, with a TEE potentially acting as an optimization or coordination layer rather than something the system fundamentally has to trust for correctness or privacy.

That separation could actually be interesting to explore like having TEE for efficient execution or coordination, ZK for independently verifiable correctness. I’ll definitely dig deeper into BCYX. There may be some interesting places where the two approaches could complement each other.