What Cloister is
A sealed-batch dark pool for Robinhood Chain stock tokens. It has two parts:
- The vault. A multi-asset note-commitment shielded pool. You deposit a stock token or a USD stablecoin and hold a note instead of a balance.
- The batch. In each epoch, notes are burned into encrypted orders. The chain sums them homomorphically, a keeper committee decrypts only the totals, and everything crosses at the first oracle price published after the batch closes.
The core insight is that equities don't need on-chain price discovery. A crypto dark pool has to find a price, which means liquidity providers, solvers or an order book. A stock token already has a reference price set on the listed market. Import it, and a sealed batch reduces to netting encrypted sums.
The design goal fits in one sentence you could defend to a regulator: privacy from the market, not from the issuer.
What it is not
- Not a mixer. Deposits come from an address you control, and every spend proves in-circuit that the deposit sits in an approved association set.
- Not custody. No entity holds your notes. Keepers hold key shares that can reveal batch totals. They can't move funds.
- Not protection from the issuer. The stock token issuer can freeze, blocklist and seize. Cloister doesn't change that.
- Not a price-discovery venue. Cloister never sets a price. If the reference is wrong, Cloister crosses at a wrong price, which is why it only trades while the feed is live.
- Not affiliated with Robinhood. Cloister is an independent specification that targets Robinhood Chain.
Common misconceptions
Vault
The vault is a fork of 0xbow-io/privacy-pools-core (Apache-2.0): Circom circuits, a Groth16 verifier, a LeanIMT commitment tree with a sliding root window, a nullifier set, and a ragequit path. The base has one pool per asset. Cloister collapses them into one tree by committing to the asset:
The spend circuit enforces conservation per asset: for each asset ID, inputs equal outputs plus any public boundary value. Asset IDs never appear as public inputs on internal spends, so an observer can't tell a stock note from a stablecoin note.
Shield
You transfer n units of an asset to the vault and it inserts C. The asset and amount are public, because this is an ERC-20 transfer. From that point on, the value belongs to a commitment.
Unshield
Tokens move to a nominated address. The amount is public again. Privacy comes from the vault's inability to link exit to entry, which grows with every holder of every asset in the tree.
For proof size, not verification speed. On a rollup, the L1 data surcharge compresses calldata and bills per compressed byte. ZK proofs are high-entropy, so they don't compress. The 256-byte proof is the deciding factor, and the ~181k-gas pairing is secondary.
Sealed batch
Time is divided into epochs. During an epoch, a trader seals an order by spending a note and publishing a vector of ciphertexts, one per (book, side) in the batch's vector group.
This is exponential ElGamal on Baby Jubjub. The order's slot encrypts its size in lots, and every other slot encrypts zero. The seal proof shows that:
- the input note exists, is unspent, and its label is in the association root
- exactly one slot is non-zero, and it matches the note's asset and the claimed side (a stablecoin note can only fund a buy, a stock note only a sell)
- the encrypted size does not exceed the note, and any remainder becomes a change note
- a private order commitment binds the size, slot and a claim secret for later
The contract adds each ciphertext into a per-slot accumulator. Point addition is cheap on the EVM, so accumulation costs a few hundred thousand gas at most for a vector group of eight books. Because every order touches every slot, the ticker and side of an order are hidden. An observer only learns that someone sealed an order into this group.
Netting only needs addition. Additively homomorphic ElGamal gets that with cheap in-circuit proofs and no external coprocessor or network. Fully homomorphic encryption would add a trust surface and a dependency to buy operations the mechanism never uses.
Clearing
When the epoch closes, keepers decrypt the totals for each book: B (stablecoin offered to buy) and S (shares offered to sell). The cross price p is the first Chainlink round whose timestamp is after the close. Every order was committed before that price existed.
Every buyer is filled for the same fraction fbuy of their order, and every seller for fsell. Each trader then submits a claim proof. It opens their private order commitment against the public (B, S, p) and mints two notes: the filled asset and the unfilled remainder. Rounding always goes down, and dust stays in the vault.
- Residual. The one-sided excess doesn't trade. By default the unfilled portion comes back as a note that can be resealed into the next batch. A v2 sweep adapter can route residuals to the public AMM as one aggregated order.
- Tolerance band. A batch voids if
pmoves more than the band (proposed 100 bps) from the reference at the epoch's open. Every order is refunded as a note. - Minimum batch. A vector group with fewer than
Nminorders doesn't clear. It rolls into the next epoch, because two-party batches leak each side's size to the other.
Keepers
A committee of n keepers holds shares of x from a distributed key generation. No single party ever knows x.
- At close, each keeper publishes
Dj = xj·A1for each accumulator, with a DLEQ proof that it used its registered share. - Any
tvalid shares combine by Lagrange interpolation tom·G = A2 − x·A1. mis recovered off-chain by baby-step giant-step. Totals are bounded to 248 lots, so this takes milliseconds.- The settler posts
m, and the contract checksm·Gagainst the combined point. Keepers can't lie about a total.
If settlement isn't posted within the liveness window, the batch voids and every order claims back its full size. A failed committee stops trading. It can't strand funds.
t colluding keepers could decrypt individual ciphertexts from the public log, including historical ones. That breaks privacy, never custody. Mitigations are a diverse committee, key rotation per era, and published keeper identities. It isn't eliminated.
Agent keys
Robinhood Chain ships with agent-driven trading, and an agent trading from a public wallet publishes its strategy one fill at a time. Cloister separates who can trade from who can withdraw.
- An order key sits below the spending key. It can seal and claim, but not unshield or transfer to a foreign note.
- The note carries a policy hash covering the allowed books, the maximum size per batch and the expiry epoch. The seal circuit enforces it.
- Claims always pay back to notes owned by the original holder. A compromised agent can make bad trades. It can't exfiltrate funds.
Interface
Published so it can be criticised before it's built. No contract with this interface exists.
// Proposed. Subject to change.
interface ICloister {
/// Vault — asset and amount public. Privacy begins after this call.
function shield(address asset, uint256 value, bytes32 precommitment)
external returns (uint256 leafIndex);
function transfer(Proof calldata p, SpendPub calldata pub) external;
function unshield(Proof calldata p, SpendPub calldata pub, address to) external;
function ragequit(Proof calldata p) external;
/// Batch — writes one ciphertext to every slot in the group.
function seal(Proof calldata p, SealPub calldata pub, Cipher[] calldata vec)
external;
/// Keepers — shares verified by DLEQ, totals verified by m·G.
function settle(uint64 batchId, Share[] calldata shares, uint64[] calldata totals)
external;
/// Pro-rata fill against public totals; mints fill and remainder notes.
function claim(Proof calldata p, ClaimPub calldata pub) external;
}
| Public input | Circuit | Description |
|---|---|---|
| stateRoot | spend · seal | A recent commitment-tree root from the sliding window. |
| assocRoot | spend · seal | The association root the deposit label must sit in. |
| nullifier | spend · seal | Spend marker. Rejected on replay, and reveals nothing about the leaf. |
| vecHash | seal | Hash of the ciphertext vector, binding the proof to what gets accumulated. |
| orderNullifier | claim | Prevents claiming the same order twice. Unlinkable to the seal. |
| batchId | seal · claim | Epoch identifier. Claims read (B, S, p) from storage by ID. |
| outCommit[2] | all | Commitments for the output notes: change, or fill and remainder. |
Each public input costs roughly 6.1k gas of scalar multiplication and 32 surcharged calldata bytes, so the set is kept deliberately small.
Corporate actions
A vault holding equities ought to break the first time a stock splits. It doesn't. ERC-8056 requires that the standard ERC-20 functions keep working with raw amounts. Splits and dividends move a display multiplier, not balances. The Chainlink feed already incorporates that multiplier.
- Raw balances don't change. The vault accounts in raw units and needs no corporate-action logic.
- Never apply the multiplier yourself. The cross price already includes it.
- Batches void while
oraclePaused()is true, and whenever the multiplier changes between a batch's open and its cross.
Reading uiMultiplier() and multiplying it into a Chainlink-derived price overstates value tenfold after a 10:1 split. It's the most dangerous integration bug in this asset class, and in a dark pool it would silently misprice an entire batch.
Treat the corporate-action path as unproven until one has been exercised on-chain for these tokens.
Disclosure
Viewing keys
A read-only key derived below the spending key rebuilds that holder's notes, sealed orders and fills. That's enough for an auditor, or for tax reporting in the EU jurisdictions where holders sit. It can't be used to spend. It's voluntary and per holder.
Association sets
Every spend and seal proves that the deposit's label sits inside an approved root, and this is verified on-chain inside the circuit. Robinhood Chain already runs real-time compliance screening, which is a natural feed for the association-set provider. A self-broadcasting user can't skip the check.
Issuer cooperation
A targeted forced-reveal or threshold-held disclosure path lets a lawful seizure act on a single note instead of every depositor. Without it, the only tool an issuer has is blocklisting the whole vault.
Privacy considerations
Privacy isn't guaranteed without care. The items below leak even when everything works correctly.
| Leak | What an observer learns | Mitigation |
|---|---|---|
| Batch totals | Crossed volume and residual per book, per batch. | Inherent. This is the print. |
| Small batches | With two participants, each learns the other's size from the total. | Minimum batch size; thin books share vector groups. |
| Participation | That someone sealed into group g at epoch e. | Relayers; the ticker and side stay hidden. |
| Keeper collusion | Individual order sizes, if t keepers decrypt raw ciphertexts. | Partial: diverse committee and key rotation. |
| Boundary amounts | The asset and exact value on shield and unshield. | Split exits across notes and time. |
| Timing correlation | Shield a size, then unshield the same size shortly after. | Delay, partial exits, holding in notes. |
| Relayer knowledge | Whoever pays your gas can link a seal to a network identity. | Multiple relayers; a single operator collapses this. |
| RPC queries | Fetching a note's sibling path tells the node which note you're about to spend. | Sync the tree locally, or query over a private transport. |
| Association proof | That you passed the check, and against which root. | Inherent. It's the price of not being a mixer. |
Threat model
| Adversary | Can | Cannot |
|---|---|---|
| Another trader | See batch totals, vault balances and every boundary amount with its timing. | Attribute an order, a fill or a holding, or see an unwind begin. |
| An informed trader | Trade on news that moves the next reference print. | Pick off a known stale price. Orders commit before the price exists. |
| The keeper committee | Halt batches by withholding shares; with t colluding, read individual orders. | Misreport a total, move funds or forge a fill. |
| The issuer | Pause, blocklist and confiscate the vault's whole balance. | Read individual positions without a viewing key or the cooperation path. |
| The sequencer | Order, delay or filter transactions; correlate submission metadata. | Reorder fills inside a batch, since every order clears at one price. |
| A relayer | Link a seal to whoever asked them to broadcast it. Censor. | Steal the note or change the order, since both are bound inside the proof. |
| The oracle | Publish a wrong or late price, and the batch crosses at it within the band. | Push a batch outside the tolerance band. That batch voids. |
Limits
None of Cloister is built or audited, no keeper committee exists, and no trusted setup has been performed. There's no address to send funds to. Anything claiming otherwise isn't this project.
Blocking
- Batches may be too thin. A dark pool with three orders an epoch leaks. The shared tree and vector groups help, but participation per epoch decides the epoch length, and it hasn't been measured.
- The committee doesn't exist. Credible, independent keepers have to be recruited before privacy means anything.
- The trusted setup is unfunded. Groth16 needs a credible ceremony for each circuit.
Structural
- It's only as good as the reference. Cloister only trades while the feed is live. Feeds that run 24/5 against a 24/7 chain mean Cloister keeps market hours.
- The moat is order flow. The base is Apache-2.0 and the design is public. As with every dark pool, the first to reach liquidity wins.
- The category may close from above. An issuer could ship its own confidential execution venue.
- The instruments are what they are. Robinhood Stock Tokens are debt securities from Robinhood Assets (Jersey) that track price without shareholder rights, and they aren't offered to U.S. persons. Regulatory treatment of both the tokens and privacy tooling is still changing.
Deployments
This table exists so it's unmistakable when it's empty. The mainnet pilot is being deployed; addresses land here the moment they exist, and nothing before then is Cloister.
| Network | Contract | Address | State |
|---|---|---|---|
| — | Vault | — | Not deployed |
| — | Batch auction | — | Not deployed |
| — | Keeper registry | — | Not deployed |
| — | Groth16 verifiers | — | Not deployed |
Audits
| Firm | Scope | Date | Report |
|---|---|---|---|
| — | No code has been submitted for review | — | None |
The upstream base has its own ceremony and review history. That doesn't transfer to a fork with modified circuits, and it won't be presented as though it does.
Parameters
These are proposed starting values. Each one gets set from the participation study, not by assertion.
| Parameter | Proposed | Trade-off |
|---|---|---|
| Epoch length | 60 s | Longer means bigger batches and more privacy, but slower fills. |
| Vector group | 8 books | Larger hides the ticker better, but costs more calldata per seal. |
Minimum batch Nmin | 8 orders | Higher leaks less, but more batches roll. |
| Tolerance band | 100 bps | Tighter protects against gaps, but more batches void. |
| Committee | t = 5 of n = 9 | A higher t resists collusion better, but a lower one keeps batches live. |
| Lot size | 1e-6 share · $0.01 | Bounds totals to 248 lots for discrete-log recovery. |
| Feed freshness | ≤ 1 round after close | Stricter pricing means more voids near feed gaps. |
Risk disclosure
Last updated: 21 September 2026
Published so that risk sits in the reading path rather than the basement. This isn't investment, legal or tax advice.
- Smart contract risk. Unwritten, unaudited code with new ZK circuits. A circuit under-constraint can mint value from nothing, and you can't detect it by reading the Solidity.
- Setup risk. Without a credible ceremony, soundness rests on whoever generated the proving key.
- Keeper risk. A colluding threshold can break order privacy, and a failing one halts batches.
- Execution risk. Fills are pro-rata and can be partial or zero. The cross price is whatever the reference prints after close, within the band.
- Oracle risk. Equity feeds pause for corporate actions and follow market hours. A wrong print inside the band is crossed.
- Privacy risk. It depends on batch participation, the size of the shared tree and user behaviour. Several leaks above have no complete mitigation.
- Issuer risk. The vault's entire balance can be frozen or seized without notice or delay.
- Regulatory risk. Stock tokens are securities in major jurisdictions and aren't offered to U.S. persons. Rules for them, for trading venues and for privacy tooling are all changing.
- Relayer risk. Relayers can censor, and can deanonymise whoever they broadcast for.
Glossary
| Term | Meaning |
|---|---|
| Note | A private record of value: asset, amount, label, nullifier seed and secret. It replaces a balance. |
| Sealed order | A note burned into a vector of ElGamal ciphertexts, one per book and side, with exactly one non-zero slot. |
| Batch | All orders sealed in one epoch, crossed together at one price. |
| Batch print | The only public output of a batch: crossed volume and residual per book, plus the price round. |
| Reference price | The first Chainlink round after close. Cloister imports it and never sets one. |
| Keeper | Holder of one share of the threshold decryption key. Can reveal sums, can't move funds. |
| Vector group | The set of books every order in a batch writes to, which hides the ticker. |
| Order key | A scoped key for agents that can seal and claim within a policy but never withdraw. |
| Nullifier | A one-time spend marker. Prevents double-spending without identifying the note. |
| Association set | The approved deposit labels a spend must prove membership of. This is what makes Cloister not a mixer. |
| Viewing key | A read-only key that rebuilds one holder's history. Shareable by design; the spending key never is. |
| uiMultiplier | The ERC-8056 display scalar for corporate actions. Never applied to an oracle price. |