Skip to content

UMBR is live on testnet. Private payments with test funds on Robinhood Chain and Tempo.

Open the app

How it works

Private payroll

Pay a whole team in one run. Each person sees their own line, the public sees a pool interaction, and you can still prove the total to your accountant.

Input
UMBR addresses + amounts
Public sees
Pool interactions only
Proof for books
Payroll total
Networks
Testnets today

Why payroll needs privacy#

A public stablecoin payroll is a published salary sheet. Anyone who knows your company wallet can list every payment, match addresses to people over time and work out who earns what, when you hire and when someone leaves. Recipients are exposed too: once their address is known, their savings and spending are readable by coworkers, landlords and strangers.

UMBR pays each person from a private balance inside the shielded pool. Amounts and the link between your balance and each recipient stay off the public record. Each person learns their own payment. Your accountant can still get the one number they need through a proof, without seeing a single salary.

Anatomy of a run#

A run is one list of payments in one asset, sent together from your private balance.

One payroll run
  1. Fund the private balance

    Shield enough of the asset (for example USDG on Robinhood Chain testnet) to cover the whole list plus a margin. The shield deposit itself is public.
  2. Load the list

    Paste or upload a CSV, or type lines by hand. The app checks every address and amount and shows the total before anything is signed.
  3. Make the proofs on your device

    Each payment is a private transfer inside the pool. Your browser builds a zero-knowledge proof for each one; your keys never leave the device.
  4. Submit

    Proofs go to the pool contract, through the relayer if you use one. Large lists are split into several transactions.
  5. Recipients sync

    Each recipient's wallet scans the pool, finds the note encrypted to them and shows the payment with its label if you added one.
  6. Keep the run record

    The run is saved locally with its id, so you can review it and prove its total later.

Run your first payroll#

  1. Open Shield and move test USDG or pathUSD into your private balance. Testnet funds come from the network's public faucet; the testnet guide walks through it.
  2. Ask each recipient for their UMBR address. They find it in the app under Receive; it starts with umbr1q.
  3. Open Payroll, pick the asset and paste your list or upload a CSV.
  4. Review the total, the line count and any warnings. Fix flagged lines before continuing.
  5. Confirm. Keep the tab open while the proofs are made; a long list can take a few minutes.
  6. When the run finishes, check each line's status in the run view.

Try it without funds

Open /app/payroll?demo=1 on a local build to walk through the screens with a pretend balance. Nothing is signed or sent in demo mode.

List format#

A list is plain CSV with the columns address,amount,label. The header row is optional; the label column is optional on every line.

march-payroll.csv
address,amount,label
umbr1qexample7x2k9...ana,4200.00,Ana - design
umbr1qexample3m8p0...ben,3850.00,Ben - backend
umbr1qexample9t4w1...chi,1200.50,"Chi - contract, March"
umbr1qexample5q7r2...dev,600
  • address is an UMBR address (umbr1q...). Public 0x addresses are rejected, because paying one would put the payment on the public record.
  • amount is a plain decimal in the run's asset, with a dot as the separator and no currency sign or thousands commas. USDG and pathUSD use 6 decimals; more precision is rejected rather than rounded.
  • label is free text that travels encrypted to the recipient as the payment memo. If it contains a comma, wrap it in double quotes. Long labels are cut to the memo limit, and the app warns you.
  • Blank lines are skipped. Zero and negative amounts are rejected.
  • The same address can appear twice (for example salary and expenses). The app flags it so you can confirm it is intended.

The last line has no label, which is fine. The app shows the parsed list before you confirm, so a column that shifted because of an unquoted comma is caught there.

The same list works from code:

payroll.ts
const lines = umbr.payroll.parseCsv(csvText);

const run = await umbr.payroll.run({ asset: "USDG", lines });
// run.id        -> keep it; you need it to prove the total later
// run.txHashes  -> one or more pool transactions
// run.lines     -> each line with its own status

Who sees what#

WhoSeesDoes not see
YouEvery line, the total, statuses and the run history on your deviceNothing is hidden from you
Each recipientTheir own amount, asset, label and settlement timeOther lines, the total, the headcount, your balance
Public explorerPool transactions at certain times, their gas payer and feeAmounts, recipients, the number of people paid, that it was payroll
Accountant with a proofThe run total, the number of payments, the asset and the settlement windowAny single amount, any address or name, your balance

Recipients can still talk to each other, and anyone a recipient shows their wallet to can see that recipient's line. Privacy on chain does not stop people sharing what they know.

Scheduled runs#

A schedule repeats a list on a calendar, such as monthly on the 25th. You set the asset, the lines, the interval and the start date. On each date the app prepares the run and asks you to confirm it.

  • Proofs need your keys, and your keys stay on your device. A schedule therefore cannot pay while your app is closed. It reminds you and prepares the run; you approve it.
  • From code, umbr.payroll.schedule stores the plan; a process you run (with its own key from a secret store) is what executes it. There is no UMBR server holding keys to do it for you.
  • Edit or cancel a schedule at any time. Past runs are not affected.
  • If the private balance is short on the date, the run waits and tells you how much to shield.

Prove the total#

Open a finished run and choose Prove total. You get a payroll-total proof stating the exact total and the number of payments, for an audience you name and an expiry of 1, 7 or 30 days. The reader checks it at umbr.trade/verify and learns nothing about individual lines. The rules behind it are in Proofs: payroll total.

The proof needs the run record, which lives on the device that sent the run. Back up your keys and run history before you clear the browser.

The relayer#

Without a relayer, the wallet that submits each pool transaction pays its gas, and that wallet appears on the explorer next to every payroll transaction. That is a pattern worth hiding: the same address touching the pool on the 25th of every month says a lot.

With the relayer on, it submits the proofs and pays the gas, and a small fee comes out of the private balance. The destination and the fee are bound inside each proof, so the relayer cannot redirect funds or take more. It can refuse or delay a submission, and it sees your IP address and timing. Turn it off in Settings if you prefer to submit yourself.

If something goes wrong#

Partial runs

A large run is split into several transactions, and each settles on its own. If one fails (a dropped connection, a relayer refusal, a pool root that moved), the lines in the other transactions are already paid. The run view marks every line as paid, pending or failed.

Retry

Choose Retry failed lines. The app rebuilds proofs only for lines that did not settle. A paid line is never sent twice, because its notes are already spent and the pool rejects a reused nullifier.

Nothing is lost

A failed transaction spends nothing. The notes it tried to use remain yours and show up again after a sync. If the tab closed mid-run, reopen Payroll; the saved run picks up where it stopped.

Testnet

Payroll runs on testnets today. Use test funds only, and read the threat model before planning anything real.