feat: pre-authorized recurring shielded withdrawals (#151) - #170
Merged
tech-adrian merged 2 commits intoAug 30, 2026
Merged
Conversation
Adds a complete bounded recurring-withdrawal system so users can pre-authorize a series of scheduled payouts from one shielded note without generating a new ZK proof for each occurrence. ## Circuit (circuits/recurring/) - New Noir circuit that proves note ownership and binds a policy tuple (recipient, max_amount, period_secs, max_uses) into an auth_commitment via a domain-separated Poseidon2 chain (RECURRING_DOMAIN = 0x52454341). - Consumes the source note nullifier on setup; creates a change note for amount - max_amount. All values 64-bit constrained; max_uses 1-255. - Nargo.toml + Prover.toml added; compiled artifact stub at frontend/src/circuits/recurring.json (replace with real nargo compile output). ## Contract (contracts/pool/src/lib.rs) - New PoolError variants: AuthNotFound, AuthRevoked, AuthExhausted, PeriodNotElapsed, AmountExceedsCap. - RecurringAuth contracttype struct stored in persistent storage. - authorize_recurring: verifies proof, consumes nullifier, inserts change note, stores auth record and recipient address. - withdraw_recurring: executes one occurrence (payout <= cap, period elapsed, uses_remaining > 0, not revoked); no proof required. - revoke_recurring: owner-signed immediate revocation. - get_recurring_auth: read-only view. - 15 new tests covering every error path and the use-count decrement loop. ## Frontend - lib/notes.ts: RecurringAuth interface + localStorage helpers with advisory locking (getRecurringAuths, saveRecurringAuth, markRecurringAuthRevoked, getActiveRecurringAuths). - lib/poseidon2.ts: computeAuthCommitment() mirrors hash_auth in-circuit. - lib/prover.ts: proveRecurringAuthorization() with all 8 circuit inputs; lazy-loads recurring.json so missing artifact only breaks the recurring flow, not the rest of the bundle. - app/recurring/page.tsx: setup UX (note selector, recipient, amount, period presets, use count, summary) + management view with on-chain state polling and one-click revocation. - app/api/relay-recurring/route.ts: rate-limited server-side trigger for cron/scheduler use; maps PeriodNotElapsed/AuthExhausted/AuthRevoked to HTTP 200 status:skipped so over-eager schedulers don't alarm. - components/Header.tsx: Recurring added to nav. ## Docs - docs/THREAT_MODEL.md: new Recurring authorizations section covering circuit guarantees, per-occurrence contract enforcement, exposure comparison vs one-shot withdrawals, and residual risks. All 154 frontend tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a complete bounded recurring-withdrawal system so users can pre-authorize a series of scheduled payouts from one shielded note without generating a new ZK proof for each occurrence.
Circuit (circuits/recurring/)
Contract (contracts/pool/src/lib.rs)
Frontend
Docs
All 154 frontend tests pass.
Summary
Why
Area
Testing done
just testpasses (contracts + frontend)just test-e2epasses, if this touches deposit/withdraw/compliance flowsfrontend/**/circuits/*.jsonartifactspnpm lint/pnpm buildpass, if this touchesfrontend/Screenshot / recording (required)
Related issue
Closes #