All posts
2026-05-26

Private payroll on Solana: paying contractors without leaking the graph

A treasury wallet paying twenty contractors every month leaks the entire payroll graph forever. Anyone with a block explorer can enumerate every recipient, every amount, every cadence. A contractor curious about peers can map the whole roster in a minute. A competitor pulling the public history can infer headcount, comp bands, and which contractor got a raise three months ago. The chain is a permanent, indexed record of relationships you almost certainly did not intend to publish. For most companies that hire on a public blockchain, that is the worst part of the deal — not the volatility, not the gas, but the unilateral disclosure of every payment relationship to anyone who asks.

This is the case SolMask was designed to solve. The protocol gives you a structural way to pay people from a treasury without creating a public on-chain edge between the treasury and the recipient. The mechanics are not exotic. The discipline is mostly operational.

What does the on-chain payroll problem actually look like?

Picture a treasury wallet Treasury7…xK holding 100,000 USDC of operating budget. On the first of every month, it sends 2,500 USDC to each of twenty contractor wallets. The Solana ledger now contains, for each of those twenty payments: sender, recipient, amount, timestamp, and a transaction signature that anyone can quote. The wallet's transaction history is a permanent enumeration of "everyone we pay, how much, when."

Three concrete leaks follow. First, every contractor can see every other contractor's address and amount the moment they look at the sender's history — peer comp is exposed by default. Second, anyone with one contractor's address (a freelancer profile, a public PR, a leaked invoice) can pivot to the treasury and from there to every other contractor. Third, the cadence itself reveals operational rhythm: a payment on the first of every month is a payroll signature; a one-off bonus is a one-off bonus. The graph tells the story even if the amounts are pseudonymous.

You cannot fix this with multiple treasuries — multiple treasuries just spread the same graph across more nodes. You cannot fix it with new contractor addresses every month — contractors would need to pre-register a fresh address each cycle, and the on-chain edge from treasury to that month's address is still visible. The only structural fix is to put a privacy layer between the treasury and the recipient.

How does a private payroll actually work with SolMask?

The pattern is the simplest one the protocol supports. Each pay cycle, the treasury deposits the gross payroll amount into the SolMask USDC pool. The pool ingests USDC, returns a note that commits to the deposited amount, and from the chain's view the treasury has interacted with a public protocol — nothing else is visible. Later, after the deposit's unlock_slot has elapsed, the treasury sends a withdraw transaction for each contractor, naming that contractor's recipient address as the destination. The withdraw transaction, from the chain's view, originates from the SolMask withdraw relayer and lands at the recipient. There is no on-chain edge from Treasury7…xK to any contractor. The graph is broken at the pool.

Step by step, with the actual operational shape:

The treasury wallet should be the wallet you already use for treasury — no new infrastructure is needed. On payday-minus-one, the treasury calls deposit with the full payroll amount. For a $50,000 payroll, that is a single 50,000 USDC deposit. Deposits are free, so the note that lands in the shielded pool commits to the full 50,000 USDC of spendable value (the protocol fee is charged later, when you pay out). Ownership of the deposit is bound to the treasury wallet itself — the note's secrets are derived deterministically from that wallet's signature, and the deposit publishes a wallet-encrypted recovery blob on-chain, so any device that reconnects the same wallet can authorize a future withdraw. There is no note file to save.

Set the privacy delay to at least one day, ideally longer. For payroll, latency is not a problem — you know the schedule months in advance. The longer the delay, the more peer deposits accumulate behind yours and the larger the anonymity set when you withdraw. A treasury that deposits on the 25th to withdraw on the 1st is doing this right; a treasury that deposits at 09:00 to withdraw at 09:15 is throwing away most of the privacy.

When the unlock slot has elapsed, generate one withdraw proof per contractor. SolMask's current circuit takes one recipient per withdraw — multi-recipient withdraws are a planned circuit upgrade — so a twenty-contractor payroll is twenty separate withdraw transactions. Each one decrements the available balance of the deposit note, produces a new note for the remainder, and emits a payment to the named recipient. The contractor sees an inbound USDC transfer from the relayer; they do not see who paid them in any chain-accessible way.

The whole flow takes about ten minutes of human time per cycle once you've done it once. The proof generation runs in the browser or on a service of your choice; the relayer handles transaction submission; the contractor sees money arrive.

What does the operational hygiene look like?

The protocol gives you privacy. Three operational practices give you reliability.

Wallet custody. With SolMask there is no separate note passphrase to manage. A deposit's spendable secrets are derived from the treasury wallet that made the deposit, and the encrypted recovery blob lives on-chain — so your entire custody surface collapses to one thing: the treasury wallet's own keys. Whoever can sign with that wallet can re-derive every note and withdraw it; whoever cannot, can't. Treat the treasury wallet exactly as you already treat a multisig signing key — hardware-backed, geographically distributed backups of the seed, the same operational severity you apply to any treasury key. Lose access to the wallet and you lose the deposits, because only that wallet can decrypt the on-chain recovery blobs. We have written about recipient-side key hygiene at /learn/choosing-a-recipient-address — the same discipline applies symmetrically to your treasury wallet.

Cross-device recovery. Because notes are recovered from the wallet plus the on-chain encrypted blobs, there is nothing to copy between machines. To run payroll from a different operator's laptop, or to recover after a machine is wiped, you connect the same treasury wallet and SolMask re-scans the pool: it pulls every recovery blob, trial-decrypts the ones belonging to your wallet, and reconstructs the full set of unspent notes. No JSON files to ship around, no shared secret to leak in transit. The only thing that must be present is the treasury wallet's signing capability — which your multisig or HSM already governs.

Accounting reconciliation. The deposit is a public on-chain transaction — your books reconcile against it normally. The withdraws are also public on-chain transactions, each visible at the relayer's transaction history, each landing at a recipient address you keep in your internal payroll system. The link between the deposit and the withdraws is what is hidden from the public, not from you. Your internal records are unchanged: you know which contractor was paid, when, how much. Your CFO can reconcile the same way they always did. The auditor can verify the cash left the treasury and arrived at the recipient; the public block explorer cannot.

What does the round-trip actually cost?

For a $50,000 monthly payroll across twenty contractors at $2,500 average per contractor, the costs break down predictably.

The deposit is free — the full $50,000 enters the shielded pool. The protocol fee is charged when you pay out: a 23 bps withdraw fee taken in USDC — that's $115 across the payroll — plus a flat 0.003 SOL per withdraw (both admin-tunable on-chain values, not hardcoded constants). The relayer pays the SOL fee up front and recoups it by keeping a small USDC tip slice out of each withdrawal, so the contractor receives the expected USDC net of the fee and your treasury never has to hold SOL for gas. At a SOL price of $60, twenty withdraws cost twenty × 0.003 SOL × $60 = $3.60.

Total round-trip cost: $118.60 on $50,000 — twenty-three and a half basis points all-in, none of it paid until you actually disburse. The full fee breakdown is at /docs/fees; deposits are free, the withdraw percentage fee is Config.withdraw_fee_bps (23 bps) and the flat withdraw fee defaults to 0.003 SOL (Config.withdraw_fee_lamports), both admin-tunable and enforced on-chain.

Compare to the alternative. Twenty international wires from a US bank to twenty independent contractors run $15-$45 per wire on the sending side, often another $15 per wire on the receiving side, plus currency conversion spreads of 1-3% on top. The same $50,000 payroll over twenty international wires runs $1,500-$4,500 in fees alone before any FX spread. SolMask's $118.60 is roughly 3% of the cheap end of that range, and you keep the privacy. The compliance posture is different in detail; the privacy posture is incomparably better.

For larger payrolls the percentage gets even better because the dominant cost is the linear 23 bps on the withdrawals, not the flat per-withdraw fee. A $500,000 payroll across the same twenty contractors costs $1,150 + $3.60 = $1,153.60 — still 23 bps all-in. The flat fees are essentially noise.

How is this defensible from a compliance standpoint?

The most common objection to private payroll is the compliance posture. The answer is structural and was designed into the protocol from day one.

The deposit instruction checks every depositor wallet against the on-chain banlist before accepting funds. The banlist is maintained from industry-standard address-risk feeds, refreshed on a regular cadence, and enforced on both the deposit and withdraw paths. A wallet that appears on the banlist cannot deposit at all. A wallet that became sanctioned after a clean deposit cannot withdraw to a banned recipient, because the recipient address is also screened at withdraw time against the same banlist. The full mechanics are at /compliance and the banlist's structure and data sources are documented at /compliance/banlist.

This is the structural compliance that makes private payroll defensible. A treasury operator using SolMask for payroll can demonstrate to their counsel that the protocol they're using does the bilateral screening at the deposit and withdraw layers — exactly the same screening a traditional payment processor does at deposit and disbursement. The privacy is privacy from the public, not from the protocol's compliance rails. The deposit instruction will reject your treasury if it is sanctioned; the withdraw instruction will reject your contractor if they are. That is the same posture as a regulated payment processor, but enforced in deterministic on-chain code instead of in an off-chain risk team's discretion.

This is also why SolMask is not the right tool for paying people you do not intend to pay legally. The screening is structural and bilateral. You cannot get a payment to a sanctioned address through the protocol any more than you could through a bank. The privacy SolMask offers is the privacy a legitimate business has every right to expect — not anonymity from law enforcement, not anonymity from sanctions enforcement, not a workaround for KYC. It is the privacy of "my payroll graph is my business, not the public's." Most regulated companies should already think about their on-chain operations that way, and very few do, because until recently the tools did not exist.

The USDC pool details are at /private/usdc; the per-pool config (including which fee_collector the deposit and withdraw fees flow to) is the same Config PDA published on-chain that we describe in the fee post.

FAQ

Q. Can I pay all twenty contractors in one transaction? A. Not today. The current withdraw circuit takes one recipient per withdraw. A twenty-contractor payroll is twenty withdraw transactions, each with its own proof, each paying its own 0.003 SOL withdraw fee. Multi-recipient withdraws are on the circuit roadmap; until they ship, the operational pattern is to generate the twenty withdraws in a batch and let the relayer submit them in parallel.

Q. Does the relayer see who I'm paying? A. The relayer sees the public inputs to your withdraw proof — Merkle root, nullifier, recipient address, amount. It does not see which deposit you are spending or which treasury originated the funds. From the relayer's view, each withdraw is an independent proof against the shielded pool. The relayer cannot link your twenty withdraws to each other beyond the trivial observation that they all happened in the same broadcast batch.

Q. What happens if I lose access to the treasury wallet? A. Then the deposits are unrecoverable — but note what changed versus older designs: there is no separate note passphrase or note file to lose. A note's secrets are derived from the treasury wallet, and the encrypted recovery blob sits on-chain, decryptable only by that wallet. So your entire recovery story collapses to "keep the treasury wallet safe," exactly as you already do for any treasury key. SolMask holds no user data and cannot recover anything on your behalf.

Q. Can I deposit weekly instead of monthly? A. Yes, and there is a privacy benefit to it. Smaller, more frequent deposits blend into a larger flow of peer deposits and produce less of a recognisable "first of the month" timing fingerprint. The cost is twenty-three bps per deposit regardless of frequency, so weekly versus monthly is no more expensive in aggregate, just more granular.

Q. Do contractors need to do anything different to receive payment? A. They give you a recipient address. That is the whole interface. They do not need a SolMask account, a wallet integration, or any knowledge that the privacy protocol exists. They receive a USDC transfer from the SolMask relayer; their wallet shows it normally. We recommend they use a fresh address for each cycle — that page is at /learn/choosing-a-recipient-address — but the protocol does not require it.

Q. How is this different from just running payroll through a payment processor? A. A payment processor knows every payment, can freeze any payment, charges 2-4% on average, and takes 1-5 business days to settle internationally. SolMask knows only what the chain knows — that a deposit happened and that withdraws happened — and the public knows even less. Settlement is instant; cost is twenty-three bps. The trade-off is that you handle the operational side (treasury-wallet custody and accounting reconciliation) instead of the processor. For a treasury that already does multisig signing and key management, that is not a step up in operational burden.

Q. Can my auditor verify the books against the chain? A. Yes. The deposit is a public transaction from your treasury to the SolMask pool — your auditor can verify the amount left the treasury and the protocol fee was paid. The withdraws are public transactions from the relayer to each contractor address — your auditor can verify each contractor received the expected amount. The link between deposit and withdraws is what is hidden from the public; your auditor has your internal payroll register, which is the same document they have always reconciled against bank withdrawals. The audit posture is unchanged. The public's view is what changed.

Private payroll on Solana: paying contractors without leaking the graph · SolMask