Skip to content

UnityNodes/Envelope

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Envelope logo

Envelope

Confidential token distributions: payroll on-chain, salaries off the record.

Send tokens to your whole team, cap table or community in one transaction,
with every amount encrypted. Public enough to audit. Private enough to be humane.

Live demo · Audit a live treasury (no wallet needed) · TokenOps SDK · ERC-7984 · Sepolia

Confidential disperse and airdrop flows for real orgs: team payroll, investor allocations, community rewards. Send configures a batch, recipients decrypt only their own allocation, auditors verify the rest.


The problem

Every ordinary token transfer publishes who received how much, forever. That makes on-chain payroll, investor allocations and grant programs a privacy disaster: one Etherscan link exposes the whole org chart, the cap table, the reward tiers. So teams retreat to spreadsheets and bank wires, and the chain loses its most obvious payments use case.

What Envelope does

Envelope is a web app for confidential batch distributions built on ERC-7984 confidential tokens and the TokenOps SDK. Amounts are encrypted in the sender's browser with FHE before they ever touch the chain, and travel as ciphertext handles that only the right people can open.

One batch transaction. Three honest views:

View Persona Sees Never sees
Send Founder / ops Templates, recipient list, live preflight checklist
Inbox Recipient Every envelope addressed to them, decrypted for their eyes only via an EIP-712 signature Anyone else's amount
Treasury Finance / auditor Distribution count, program breakdown, recipient counts, on-chain proofs Any individual amount

Templates, not CSV dumps

Distributions start from a template that matches how orgs actually pay people:

  • Team payroll: monthly salaries; period metadata, e.g. Payroll · July 2026
  • Investor allocation: SAFT tranches; round metadata, e.g. Allocation · Seed
  • Community rewards: grants and campaigns, e.g. Rewards · Testnet Season 3

The template label is deliberately public (that's the auditability half of the story); the amounts are deliberately not (that's the confidentiality half).

How it works

sender browser                     Sepolia                        recipient browser
──────────────                     ───────                        ─────────────────
amounts (plaintext)
   │  FHE encrypt (WASM, in-browser)
   ▼
ciphertext handles + ZK input proof
   │
   ▼
DisperseConfidential.disperseConfidentialTokenDirect()
   │        one tx, N confidential ERC-7984 transfers
   │        emits DirectDistribution(recipients, encrypted handles)
   ▼
EnvelopeRegistry.logDistribution()
            public metadata notary: template, label,
            recipient count, zero amount data
                                                            read events addressed to me
                                                               │  EIP-712 signature
                                                               ▼
                                                            userDecrypt → my amount, my eyes only
  • Encryption / decryption: @zama-fhe/sdk v3 relayer; plaintext never leaves the browser.
  • Batch transfer: TokenOps fhe-disperse singleton (0x710d…DBb4 on Sepolia), direct mode: one setOperator approval, then one tx per distribution.
  • Metadata: EnvelopeRegistry.sol, a 23-line event-only notary. It holds no funds and knows no amounts; it exists so recipients and auditors can reconstruct the timeline purely from logs.
  • Token: CTTT, the ERC-7984 confidential test token (open faucet mint built into the app).

Quick start

git clone <this repo> envelope && cd envelope/web
npm install
npm run dev

Open http://localhost:3000, connect a Sepolia wallet, and use the built-in “Mint 100,000 test CTTT” helper on the review screen. You'll need a little Sepolia ETH for gas (faucet).

No environment needed: the deployed registry and a Sepolia RPC that serves eth_getLogs are the defaults. Optional overrides in web/.env.local:

NEXT_PUBLIC_RPC_URL=https://sepolia.drpc.org   # must allow eth_getLogs (publicnode paywalls it)
NEXT_PUBLIC_REGISTRY_ADDRESS=0x…               # your own EnvelopeRegistry deployment
NEXT_PUBLIC_REGISTRY_DEPLOY_BLOCK=…            # its deploy block (getLogs scan start)

Contracts

cd contracts
forge test
forge script script/Deploy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast

End-to-end scripts

scripts/ contains the Node E2E proofs used during development (Node ≥ 22):

cd scripts && npm install
node gate-a.mjs   # faucet mint → operator approval → preflight → confidential disperse
node gate-b.mjs   # EIP-712 user decryption of an encrypted balance/amount

Deployed on Sepolia

Live demo: envelope.unitynodes.com

Contract Address
EnvelopeRegistry 0xe2ce78FAF1C3b8b8faec6ea00053720b3D450789
DisperseConfidential (TokenOps) 0x710dD9885Cc9986EfD234E7719483147a6d8DBb4
CTTT (ERC-7984) 0x258F9D60dc023870e4E3109c894D834D5377361a

Example distributions on Sepolia: community rewards · team payroll. Amounts in the event logs are FHE ciphertext handles.

What the treasurer view proves (and what it can't see)

The registry + disperse events give an auditor, per organization: when each batch went out, under which program, to how many wallets, with a transaction proof for each. The amounts exist on-chain only as FHE ciphertext handles under the recipients' access control. There is no admin key, no backdoor decrypt, no “trust us”. The privacy boundary is the protocol's ACL, not this app.

Threat model, stated plainly

Metadata is the audit half; amounts are what we seal. We are precise about where the privacy line falls, because on a confidential-token bounty that line is the whole product.

  • Amounts are the secret. On-chain they exist only as FHE ciphertext under each recipient's ACL. Nobody can decrypt someone else's payout: not the sender, not this app.
  • Recipient addresses are public by protocol, not by our choice. ERC-7984 confidential transfers move ciphertext to a known to address, so the address is on-chain in plaintext. OpenZeppelin's audit of the TokenOps confidential airdrop states this outright: recipient addresses are not hidden, "this is a property of the underlying asset, which is an ERC-7984 Confidential Token." Any project claiming it hides recipient lists on a disperse flow is misleading you. Envelope seals what can be sealed (the amounts) and tells you exactly where the boundary is.
  • Metadata is deliberately public. Sender, recipient set, count, template type, label and timing are plaintext events. That's a feature, not a leak: it's what lets a finance team verify payouts happened on schedule without seeing a single figure. Anything shown in Treasury is already readable on Etherscan.
  • The label is public forever. It should name the program, not the counterparty: Payroll · July 2026 is fine; Seed round · Acme Ventures is not. The send flow warns about this where the label is composed, and the label is optional.
  • Metadata can deanonymize small groups. “Team payroll, 2 recipients, monthly” plus timing correlation can suggest whose wallets these are even with sealed amounts. That is an EVM-level property, not something an app layer can patch, so we say it plainly instead of pretending otherwise.

Stack

Next.js 14 · Tailwind v4 · wagmi 3 + viem · @tokenops/sdk (fhe-disperse, testnet-faucet) · @zama-fhe/sdk + @zama-fhe/react-sdk v3 · Foundry

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors