Confidential multi-token payroll on Zama FHEVM (ERC-7984)
Hushroll is a full-stack payroll dApp where salaries, net pay, and balances remain encrypted on-chain, while authorized parties can decrypt only what they are permitted to view.
Watch the Hushroll demo: https://youtu.be/6pwt6757pYc?si=BqoqzBM7lvFTK9-C
- Why Hushroll
- Current Scope
- Payroll Modes
- Architecture
- How It Works
- Additional Documentation
- Product Direction
- Contracts
- Frontend
- Subgraph
- Local Setup
- Testing
- Deployment Addresses
- Repository Layout
- License
Traditional payroll systems expose sensitive compensation data to too many systems and people. Hushroll uses FHE-powered confidential tokens so payroll operations stay practical without leaking private amounts.
- Confidential compensation flows: salary amounts and balances are encrypted by default.
- Real payroll operations: onboarding, salary updates, single payments, batch payroll, approvals, invoices, receipts, and history.
- Multi-role product model: employer, employee, approver, and manager workspaces.
- Auditability without plaintext leakage: indexed events and analytics through The Graph.
- Employer registration via
PayrollFactory(per-employerConfidentialPayrolldeployment). - Multi-token payroll support in one contract (
cUSDCandcUSDT). - Employee onboarding, updates, removal.
- Single payment request flow.
- Batch payment request flow.
- Wrap (
USDC/USDT -> cUSDC/cUSDT) and unwrap flows. - Token activity tracking (wrap/unwrap events) in dedicated UI tab.
- Employee invoice submission with encrypted metadata and attachments.
- Employer invoice review, approval, rejection, and payment initiation flow.
- Solo and multi-sig payroll modes.
- Configurable approvers and required approval threshold.
- Approve/reject support for both single and batch requests.
- Threshold-based rejection voting model.
- Department create/update/deactivate.
- Assign/remove employees to departments.
- Department manager detection and manager UI.
- Department-scoped operations available under current contract permissions.
- Role-aware routes and sidebars.
- Paginated employees/payments/approvals/token activity tables.
- Employer payroll export.
- Employee payment history + receipt download.
- Dedicated employee and employer invoice workspaces.
- Redirect to homepage when wallet disconnects.
Hushroll supports two operating models so teams can choose speed or governance depth.
| Dimension | Solo Payroll | Multi-sig Payroll |
|---|---|---|
| Intended use | Founder-led or small teams | Finance/compliance-controlled teams |
| Approver set | Single approver (typically employer) | Multiple approvers |
| Required approvals | 1 |
Configurable threshold (M-of-N) |
| Single payment behavior | Executes immediately after request | Stays pending until approval threshold is reached |
| Batch payroll behavior | Executes immediately after batch request | Stays pending until approval threshold is reached |
| Rejection model | Not practically used in 1-of-1 mode |
Threshold-based rejection voting |
| UI focus | Payroll operations and execution speed | Approval queues, badges, and governance workflow |
- Designed for fast execution with minimal coordination overhead.
- Useful for early-stage teams where one operator handles payroll.
- Designed for shared control and internal governance.
- Useful when payroll actions require reviewer/approver checkpoints before execution.
Wallets (Employer/Employee/Approver/Manager)
|
v
Frontend (Next.js + wagmi + FHE hooks)
|
+--> PayrollFactory
| |
| +--> ConfidentialPayroll per employer
| + employee management
| + scheduler (single/batch)
| + approvals (multi-sig)
| + invoices
| + departments
|
+--> ConfidentialUSDC / ConfidentialUSDT (ERC-7984)
| + wrap / unwrap / confidential transfer
|
+--> Subgraph (The Graph)
+ indexed events
+ aggregates
+ GraphQL data for dashboards/history/analytics
sequenceDiagram
participant EmployerWallet as Employer Wallet
participant Frontend
participant Factory as PayrollFactory
participant Payroll as ConfidentialPayroll
EmployerWallet->>Frontend: Configure solo/multi-sig
Frontend->>Factory: registerEmployer(approvers, requiredApprovals)
Factory->>Payroll: deploy ConfidentialPayroll(tokens, employer, approvers, required)
Payroll-->>Factory: payrollAddress
Factory-->>Frontend: PayrollCreatedMultiToken event
Frontend->>Factory: employerPayroll(employer)
Factory-->>Frontend: payrollAddress
sequenceDiagram
participant Employer
participant Frontend
participant Payroll as ConfidentialPayroll
participant Token as cUSDC/cUSDT
participant Employee
Employer->>Frontend: Onboard employee with encrypted salary
Frontend->>Payroll: onboardEmployee(employee, encryptedSalary, proof, frequency)
Employer->>Frontend: Create payment request
Frontend->>Payroll: createPaymentRequest(employee, token)
alt requiredApprovals == 1
Payroll->>Token: confidentialTransferFrom(employer, employee, encryptedNetSalary)
Payroll-->>Frontend: SalaryPaid + PaymentReceipt
else requiredApprovals > 1
Payroll-->>Frontend: PaymentRequestCreated(PENDING)
Note over Frontend,Payroll: Approvers vote until threshold
Payroll->>Token: confidentialTransferFrom(...) on threshold
Payroll-->>Frontend: SalaryPaid + PaymentReceipt
end
Employee->>Frontend: Decrypt own balance/receipt
sequenceDiagram
participant Employer
participant Payroll as ConfidentialPayroll
participant ApproverA as Approver A
participant ApproverB as Approver B
participant Employees
Employer->>Payroll: createBatchPaymentRequest(token)
alt requiredApprovals == 1
Payroll->>Employees: Execute confidential transfers for active employees
Payroll-->>Employer: BatchPayrollExecuted
else requiredApprovals > 1
Payroll-->>Employer: BatchPayrollCreated(PENDING)
ApproverA->>Payroll: approveBatchPayment(requestId)
ApproverB->>Payroll: approveBatchPayment(requestId)
Payroll->>Employees: Execute confidential transfers at threshold
Payroll-->>Employer: BatchPayrollExecuted
end
sequenceDiagram
participant User
participant ERC20 as USDC/USDT
participant CToken as cUSDC/cUSDT
participant Subgraph
participant UI
User->>ERC20: approve(wrapper, amount)
User->>CToken: wrap(user, amount)
CToken-->>Subgraph: ConfidentialTransfer(from=0x0,to=user,amountHandle)
Subgraph-->>UI: TokenActivity(WRAP)
User->>CToken: unwrap(from=user,to=user,encryptedAmount,proof)
CToken-->>Subgraph: UnwrapRequested(receiver, encryptedAmount)
CToken-->>Subgraph: UnwrapFinalized(receiver, encryptedAmount, clearAmount)
Subgraph-->>UI: TokenActivity(UNWRAP_REQUEST / UNWRAP_FINALIZED)
sequenceDiagram
participant Employee
participant Frontend
participant Storage as Storacha / IPFS
participant Payroll as ConfidentialPayroll
participant Employer
participant Approvers
Employee->>Frontend: Fill invoice form + optional attachments
Frontend->>Storage: Upload AES-encrypted metadata + attachments
Frontend->>Payroll: uploadInvoice(invoiceId, metadataCID, attachmentCID, token, encryptedAmount)
Payroll-->>Employer: InvoiceSubmitted
Employer->>Frontend: Review encrypted invoice details
alt rejected
Frontend->>Payroll: rejectInvoice(invoiceId)
Payroll-->>Employee: InvoiceRejected
else approved
Frontend->>Payroll: approveInvoice(invoiceId)
Frontend->>Payroll: createPaymentWithInvoice(employee, invoiceId)
alt requiredApprovals == 1
Payroll-->>Employee: InvoicePaid
else requiredApprovals > 1
Payroll-->>Approvers: PaymentRequestCreated(PENDING)
Approvers->>Payroll: approve payment request
Payroll-->>Employee: InvoicePaid
end
end
flowchart LR
A[Payroll & Token Events] --> B[Subgraph Mappings]
B --> C[(Entities + Aggregates)]
C --> D[GraphQL Queries]
D --> E[Frontend Hooks]
E --> F[Dashboards, Payments, Approvals, Analytics]
- Architecture deep dive:
docs/ARCHITECTURE.md - Security model:
docs/SECURITY_MODEL.md - Feature status matrix:
docs/FEATURE_MATRIX.md
| Feature | Current State | Next Step |
|---|---|---|
| Invoice workflows | End-to-end encrypted invoice lifecycle | Tighten invoice analytics, payment linkage, and reporting UX |
| Deductions | Contract support + partial UI/indexing | Complete deduction setup/consent/payment analytics flow |
| Automated payroll by frequency | Scheduler primitives available | Auto-run payroll at configured cadence with execution controls |
| Advanced manager policy model | Core manager operations available | Richer permission model and deeper manager workflows |
| Analytics depth | Strong baseline | Extended enterprise KPIs and reporting dimensions |
Core contracts live in hardhat/contracts:
factory/PayrollFactory.solConfidentialPayroll.solmodules/PayrollEmployeeManagement.solmodules/PayrollScheduler.solmodules/PayrollDepartmentManager.solmodules/PayrollDeductionManager.solmodules/PayrollInvoiceManager.soltokens/ConfidentialUSDC.soltokens/ConfidentialUSDT.sol
App routes in frontend/src/app:
onboarding- employer setupemployer- dashboard, employees, departments, payments, approvals, invoices, analyticsemployee- dashboard, payments, receipts, invoicesmanager- department manager workspaceapprover- approval workspace
Key hooks:
useConfidentialPayroll.tsuseConfidentialToken.tsuseEmployer.tsuseEmployee.tsuseInvoiceManagement.tsuseManagerStatus.tsuseSubgraph.ts
subgraph/ indexes:
- employers, employees, departments
- payment requests, approvals, executed payments, invoices
- token activity (wrap/unwrap)
- aggregate counters for dashboard/analytics
Main files:
subgraph/subgraph.yamlsubgraph/schema.graphqlsubgraph/src/factory.tssubgraph/src/payroll.tssubgraph/src/token.ts
- Node.js 20+
- npm
- Docker (for local graph node)
- MetaMask (or compatible wallet)
cd hardhat
npm install
npx hardhat nodeIn another terminal:
cd hardhat
npm run deploy:localhostcd frontend
npm install
npm run devcd subgraph
docker compose up -d
npm install
npm run codegen
npm run build
npm run create-local
npm run deploy-localFrom hardhat/:
npm run compile
npm test| Test Suite | Scope Covered |
|---|---|
ConfidentialUSDC.test.ts |
Wrapper deployment, wrap flow, encrypted balance checks, unwrap request path, confidential transfers |
PayrollFactory.test.ts |
Factory token wiring, employer registration, invalid registration guards, operator management calls |
Payroll.test.ts |
End-to-end payment behavior across cUSDC/cUSDT, salary updates, single + batch payment execution |
PayrollEmployeeManagement.test.ts |
Employee onboarding, updates, removals, employer-only restrictions |
PayrollScheduler.test.ts |
Single/batch request lifecycle, threshold approvals/rejections, cross-vote guards, unsupported-token protection |
PayrollDepartmentManager.test.ts |
Department creation, manager/employer scoped assignment behavior, metadata validation, deactivation guards, manager scope enforcement |
PayrollDeductionManager.test.ts |
Deduction add/consent/update/remove lifecycle, authorization guards |
PayrollInvoiceManager.test.ts |
Invoice preconditions, missing invoice failure paths, upload-path regression guard |
Current local status:
- 42 passing tests (
npm testinhardhat/)
Current Sepolia deployment from deployments.json:
| Contract Name | Address | Explorer Link |
|---|---|---|
| PayrollFactory | 0x324D089b1Df081790D49e10548013579B51107f6 |
View on Etherscan |
ConfidentialUSDC (cUSDC) |
0x3AC9786723325C84F47591EE47760b337187C240 |
View on Etherscan |
ConfidentialUSDT (cUSDT) |
0x06a53e36C046Bb9c5fC1b460a163d40e62F6222b |
View on Etherscan |
| USDC (Sepolia) | 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 |
View on Etherscan |
| USDT (Sepolia) | 0x93C5d30a7509E60871B77A3548a5BD913334cd35 |
View on Etherscan |
Hushroll/
├── frontend/
├── hardhat/
├── subgraph/
├── docs/
└── README.md
MIT - see LICENSE
