Payshade
One transaction. Zero identity leaks.
Created on 23rd August 2026
•
Payshade
One transaction. Zero identity leaks.
The problem Payshade solves
On Ethereum, payroll is public surveillance. When a company pays salaries on chain, every transfer is permanently visible: who got paid, how much, how often. Anyone (competitors, analytics firms, stalkers) can link an employee's wallet to their identity and see exactly what they earn. Traditional crypto payroll forces a brutal choice: leak everyone's salary, or pay people off chain and lose the auditability and composability that made you use crypto in the first place.
What Payshade does about it
It uses ERC-5564 stealth addresses plus the ERC-6538 meta-address registry so payments are real on-chain transfers, but unlinkable to recipients:
Employee registers once. They sign locally; a spending key and viewing key derive in-browser, and only a public key goes into the shared ERC-6538 registry.
Company pays in bulk. The payrun derives a fresh one-time stealth address per employee client-side (from their meta-address) and pays everyone in a single PayshadeCore contract call (about 46 lines by design).
Nobody can connect the dots. Observers see N transfers to N random addresses. No pattern links them to each other or to any identity. Amounts per person are hidden too.
Only the employee can find their money. They scan announcements locally with their viewing key, then sweep funds from each stealth address into their real wallet privately.
So employees get salary privacy without trusting a custodian or mixer, and companies get one-transaction payroll with no server, no KYC, no accounts. Fully non-custodial.
Challenges I ran into
The one that cost me the most time was a React crash I caused myself. On the company page, every recipient row checks the ERC-6538 registry live and shows a little registered / not registered yet badge. My first version did if (!address) return null before calling useReadContract — React does not like that at all, so the whole payrun page crashed whenever rows were added or removed mid-edit. Took an embarrassing number of console errors to realize it was the Rules of Hooks biting me. Rewrote it so hooks always run unconditionally and the query is just gated with enabled.
Another sneaky one: refreshing the Employee page made it look like you'd never set up private payments, even though you registered on-chain earlier. Turns out wagmi restores the wallet connection asynchronously, and I was reading keys/payment history from storage during the very first render — while the address was still undefined. Moved all rehydration into an effect keyed on (address, chainId) and it went away. Cool side effect though: my stealth keys are derived deterministically from a signature, so even if the browser wipes session storage, signing the same message regenerates the exact same keys. A bug turned into self-healing.
And the classic deploy one: everything worked locally, pushed to Vercel, homepage fine — then refreshing /company 404'd, because the SPA lives inside an npm-workspaces monorepo and nothing told Vercel about client-side routes. Fixed with vercel.json rewrites to index.html plus moving the app under app/ so Vercel's monorepo detection picks the right root.
Tracks Applied (2)
Private Wallets and Payments
Devcon Tickets
Technologies used
Cheer Project
Cheering for a project means supporting a project you like with as little as 0.0025 ETH. Right now, you can Cheer using ETH on Arbitrum, Optimism and Base.